From 0f0c17111d970f77e5582669a0bd180fcf3ad05a Mon Sep 17 00:00:00 2001 From: Friedemann Kleint <Friedemann.Kleint@nokia.com> Date: Mon, 23 Aug 2010 12:15:48 +0200 Subject: [PATCH] VCS[git]: Add menu option for git amend. Task-number: QTCREATORBUG-1642 --- src/libs/utils/submiteditorwidget.cpp | 17 +++- src/libs/utils/submiteditorwidget.h | 6 ++ src/plugins/git/commitdata.cpp | 1 + src/plugins/git/commitdata.h | 1 + src/plugins/git/gitclient.cpp | 99 ++++++++++++++------- src/plugins/git/gitclient.h | 6 +- src/plugins/git/gitplugin.cpp | 29 +++++- src/plugins/git/gitplugin.h | 5 +- src/plugins/git/gitsubmiteditor.cpp | 1 - src/plugins/vcsbase/vcsbasesubmiteditor.cpp | 11 +++ src/plugins/vcsbase/vcsbasesubmiteditor.h | 5 ++ 11 files changed, 139 insertions(+), 42 deletions(-) diff --git a/src/libs/utils/submiteditorwidget.cpp b/src/libs/utils/submiteditorwidget.cpp index 99f1d60c8ba..b4a5be89341 100644 --- a/src/libs/utils/submiteditorwidget.cpp +++ b/src/libs/utils/submiteditorwidget.cpp @@ -142,6 +142,7 @@ struct SubmitEditorWidgetPrivate bool m_filesChecked; int m_fileNameColumn; int m_activatedRow; + bool m_emptyFileListEnabled; QList<AdditionalContextMenuAction> descriptionEditContextMenuActions; QVBoxLayout *m_fieldLayout; @@ -154,6 +155,7 @@ SubmitEditorWidgetPrivate::SubmitEditorWidgetPrivate() : m_filesChecked(false), m_fileNameColumn(1), m_activatedRow(-1), + m_emptyFileListEnabled(false), m_fieldLayout(0), m_lineWidth(defaultLineWidth) { @@ -446,7 +448,7 @@ void SubmitEditorWidget::updateActions() void SubmitEditorWidget::updateSubmitAction() { const unsigned checkedCount = checkedFilesCount(); - const bool newFilesCheckedState = checkedCount; + const bool newFilesCheckedState = m_d->m_emptyFileListEnabled || checkedCount > 0; // Emit signal to update action if (m_d->m_filesChecked != newFilesCheckedState) { m_d->m_filesChecked = newFilesCheckedState; @@ -581,6 +583,19 @@ void SubmitEditorWidget::fileListCustomContextMenuRequested(const QPoint & pos) } } +bool SubmitEditorWidget::isEmptyFileListEnabled() const +{ + return m_d->m_emptyFileListEnabled; +} + +void SubmitEditorWidget::setEmptyFileListEnabled(bool e) +{ + if (e != m_d->m_emptyFileListEnabled) { + m_d->m_emptyFileListEnabled = e; + updateSubmitAction(); + } +} + } // namespace Utils #include "submiteditorwidget.moc" diff --git a/src/libs/utils/submiteditorwidget.h b/src/libs/utils/submiteditorwidget.h index e31fb8cd3cf..457783a9d7f 100644 --- a/src/libs/utils/submiteditorwidget.h +++ b/src/libs/utils/submiteditorwidget.h @@ -76,6 +76,8 @@ class QTCREATOR_UTILS_EXPORT SubmitEditorWidget : public QWidget Q_PROPERTY(QAbstractItemView::SelectionMode fileListSelectionMode READ fileListSelectionMode WRITE setFileListSelectionMode DESIGNABLE true) Q_PROPERTY(bool lineWrap READ lineWrap WRITE setLineWrap DESIGNABLE true) Q_PROPERTY(int lineWrapWidth READ lineWrapWidth WRITE setLineWrapWidth DESIGNABLE true) + Q_PROPERTY(bool emptyFileListEnabled READ isEmptyFileListEnabled WRITE setEmptyFileListEnabled DESIGNABLE true) + public: explicit SubmitEditorWidget(QWidget *parent = 0); virtual ~SubmitEditorWidget(); @@ -91,6 +93,10 @@ public: QString descriptionText() const; void setDescriptionText(const QString &text); + // 'Commit' action enabled despite empty file list + bool isEmptyFileListEnabled() const; + void setEmptyFileListEnabled(bool e); + int fileNameColumn() const; void setFileNameColumn(int c); diff --git a/src/plugins/git/commitdata.cpp b/src/plugins/git/commitdata.cpp index 0a160eff2d4..10a39b77f44 100644 --- a/src/plugins/git/commitdata.cpp +++ b/src/plugins/git/commitdata.cpp @@ -82,6 +82,7 @@ void CommitData::clear() { panelInfo.clear(); panelData.clear(); + amendSHA1.clear(); stagedFiles.clear(); unstagedFiles.clear(); diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h index f0380c4dbc2..6b8b99971e5 100644 --- a/src/plugins/git/commitdata.h +++ b/src/plugins/git/commitdata.h @@ -80,6 +80,7 @@ struct CommitData QStringList stagedFileNames(const QString &stateFilter = QString()) const; QStringList unstagedFileNames(const QString &stateFilter = QString()) const; + QString amendSHA1; GitSubmitEditorPanelInfo panelInfo; GitSubmitEditorPanelData panelData; diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp index d2293d55668..c15817db9ef 100644 --- a/src/plugins/git/gitclient.cpp +++ b/src/plugins/git/gitclient.cpp @@ -1336,14 +1336,15 @@ void GitClient::launchGitK(const QString &workingDirectory) } bool GitClient::getCommitData(const QString &workingDirectory, + bool amend, QString *commitTemplate, - CommitData *d, + CommitData *commitData, QString *errorMessage) { if (Git::Constants::debug) qDebug() << Q_FUNC_INFO << workingDirectory; - d->clear(); + commitData->clear(); // Find repo const QString repoDirectory = GitClient::findRepositoryForDirectory(workingDirectory); @@ -1352,7 +1353,7 @@ bool GitClient::getCommitData(const QString &workingDirectory, return false; } - d->panelInfo.repository = repoDirectory; + commitData->panelInfo.repository = repoDirectory; QDir gitDir(repoDirectory); if (!gitDir.cd(QLatin1String(kGitDirectoryC))) { @@ -1365,13 +1366,14 @@ bool GitClient::getCommitData(const QString &workingDirectory, if (QFileInfo(descriptionFile).isFile()) { QFile file(descriptionFile); if (file.open(QIODevice::ReadOnly|QIODevice::Text)) - d->panelInfo.description = commandOutputFromLocal8Bit(file.readAll()).trimmed(); + commitData->panelInfo.description = commandOutputFromLocal8Bit(file.readAll()).trimmed(); } // Run status. Note that it has exitcode 1 if there are no added files. bool onBranch; QString output; - switch (gitStatus(repoDirectory, true, &output, errorMessage, &onBranch)) { + const StatusResult status = gitStatus(repoDirectory, true, &output, errorMessage, &onBranch); + switch (status) { case StatusChanged: if (!onBranch) { *errorMessage = tr("You did not checkout a branch."); @@ -1379,6 +1381,8 @@ bool GitClient::getCommitData(const QString &workingDirectory, } break; case StatusUnchanged: + if (amend) + break; *errorMessage = msgNoChangedFiles(); return false; case StatusFailed: @@ -1403,42 +1407,71 @@ bool GitClient::getCommitData(const QString &workingDirectory, // # // # list of files... - if (!d->parseFilesFromStatus(output)) { - *errorMessage = msgParseFilesFailed(); - return false; - } - // Filter out untracked files that are not part of the project - VCSBase::VCSBaseSubmitEditor::filterUntrackedFilesOfProject(repoDirectory, &d->untrackedFiles); - if (d->filesEmpty()) { - *errorMessage = msgNoChangedFiles(); - return false; + if (status != StatusUnchanged) { + if (!commitData->parseFilesFromStatus(output)) { + *errorMessage = msgParseFilesFailed(); + return false; + } + // Filter out untracked files that are not part of the project + VCSBase::VCSBaseSubmitEditor::filterUntrackedFilesOfProject(repoDirectory, &commitData->untrackedFiles); + if (commitData->filesEmpty()) { + *errorMessage = msgNoChangedFiles(); + return false; + } } - d->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name")); - d->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email")); + commitData->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name")); + commitData->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email")); - // Get the commit template - QString templateFilename = readConfigValue(workingDirectory, QLatin1String("commit.template")); - if (!templateFilename.isEmpty()) { - // Make relative to repository - const QFileInfo templateFileInfo(templateFilename); - if (templateFileInfo.isRelative()) - templateFilename = repoDirectory + QLatin1Char('/') + templateFilename; - QFile templateFile(templateFilename); - if (templateFile.open(QIODevice::ReadOnly|QIODevice::Text)) { - *commitTemplate = QString::fromLocal8Bit(templateFile.readAll()); - } else { - qWarning("Unable to read commit template %s: %s", - qPrintable(templateFilename), - qPrintable(templateFile.errorString())); + // Get the commit template or the last commit message + if (amend) { + // Amend: get last commit data as "SHA1@message". TODO: Figure out codec. + QStringList args(QLatin1String("log")); + args << QLatin1String("--max-count=1") << QLatin1String("--pretty=format:%h@%B"); + const Utils::SynchronousProcessResponse sp = synchronousGit(repoDirectory, args); + if (sp.result != Utils::SynchronousProcessResponse::Finished) { + *errorMessage = tr("Unable to retrieve the last commit data from %1.").arg(repoDirectory); + return false; + } + const int separatorPos = sp.stdOut.indexOf(QLatin1Char('@')); + QTC_ASSERT(separatorPos != -1, return false) + commitData->amendSHA1= sp.stdOut.left(separatorPos); + *commitTemplate = sp.stdOut.mid(separatorPos + 1); + } else { + // Commit: Get the commit template + QString templateFilename = readConfigValue(workingDirectory, QLatin1String("commit.template")); + if (!templateFilename.isEmpty()) { + // Make relative to repository + const QFileInfo templateFileInfo(templateFilename); + if (templateFileInfo.isRelative()) + templateFilename = repoDirectory + QLatin1Char('/') + templateFilename; + QFile templateFile(templateFilename); + if (templateFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + *commitTemplate = QString::fromLocal8Bit(templateFile.readAll()); + } else { + qWarning("Unable to read commit template %s: %s", + qPrintable(templateFilename), + qPrintable(templateFile.errorString())); + } } } return true; } +// Log message for commits/amended commits to go to output window +static inline QString msgCommitted(const QString &amendSHA1, int fileCount) +{ + if (amendSHA1.isEmpty()) + return GitClient::tr("Committed %n file(s).\n", 0, fileCount); + if (fileCount) + return GitClient::tr("Amended %1 (%n file(s)).\n", 0, fileCount).arg(amendSHA1); + return GitClient::tr("Amended %1.").arg(amendSHA1); +} + // addAndCommit: bool GitClient::addAndCommit(const QString &repositoryDirectory, const GitSubmitEditorPanelData &data, + const QString &amendSHA1, const QString &messageFile, const QStringList &checkedFiles, const QStringList &origCommitFiles, @@ -1447,6 +1480,7 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory, if (Git::Constants::debug) qDebug() << "GitClient::addAndCommit:" << repositoryDirectory << checkedFiles << origCommitFiles; const QString renamedSeparator = QLatin1String(" -> "); + const bool amend = !amendSHA1.isEmpty(); // Do we need to reset any files that had been added before // (did the user uncheck any previously added files) @@ -1483,7 +1517,8 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory, QStringList args; args << QLatin1String("commit") << QLatin1String("-F") << QDir::toNativeSeparators(messageFile); - + if (amend) + args << QLatin1String("--amend"); const QString &authorString = data.authorString(); if (!authorString.isEmpty()) args << QLatin1String("--author") << authorString; @@ -1492,7 +1527,7 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory, QByteArray errorText; const bool rc = fullySynchronousGit(repositoryDirectory, args, &outputText, &errorText); if (rc) { - outputWindow()->append(tr("Committed %n file(s).\n", 0, checkedFiles.size())); + outputWindow()->append(msgCommitted(amendSHA1, checkedFiles.size())); } else { outputWindow()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(commandOutputFromLocal8Bit(errorText))); } diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h index ef0ca8dac85..11162a93414 100644 --- a/src/plugins/git/gitclient.h +++ b/src/plugins/git/gitclient.h @@ -185,13 +185,13 @@ public: StashResult ensureStash(const QString &workingDirectory, QString *errorMessage); StashResult ensureStash(const QString &workingDirectory); - bool getCommitData(const QString &workingDirectory, - QString *commitTemplate, - CommitData *d, + bool getCommitData(const QString &workingDirectory, bool amend, + QString *commitTemplate, CommitData *commitData, QString *errorMessage); bool addAndCommit(const QString &workingDirectory, const GitSubmitEditorPanelData &data, + const QString &amendSHA1, const QString &messageFile, const QStringList &checkedFiles, const QStringList &origCommitFiles, diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp index efc7af9b2fa..404869ce6dd 100644 --- a/src/plugins/git/gitplugin.cpp +++ b/src/plugins/git/gitplugin.cpp @@ -451,6 +451,10 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage) globalcontext, true, SLOT(startCommit())); actionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C"))); + createRepositoryAction(actionManager, gitContainer, + tr("Amend Last Commit..."), QLatin1String("Git.AmendCommit"), + globalcontext, true, SLOT(startAmendCommit())); + actionCommand = createRepositoryAction(actionManager, gitContainer, tr("Push"), QLatin1String("Git.Push"), globalcontext, true, SLOT(push())); @@ -610,8 +614,19 @@ void GitPlugin::unstageFile() m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile())); } +void GitPlugin::startAmendCommit() +{ + startCommit(true); +} + void GitPlugin::startCommit() { + startCommit(false); +} + +void GitPlugin::startCommit(bool amend) +{ + if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor()) return; if (isCommitEditorOpen()) { @@ -624,7 +639,7 @@ void GitPlugin::startCommit() QString errorMessage, commitTemplate; CommitData data; - if (!m_gitClient->getCommitData(state.topLevel(), &commitTemplate, &data, &errorMessage)) { + if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) { VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage); return; } @@ -632,6 +647,7 @@ void GitPlugin::startCommit() // Store repository for diff and the original list of // files to be able to unstage files the user unchecks m_submitRepository = data.panelInfo.repository; + m_commitAmendSHA1 = data.amendSHA1; m_submitOrigCommitFiles = data.stagedFileNames(); m_submitOrigDeleteFiles = data.stagedFileNames("deleted"); @@ -651,10 +667,10 @@ void GitPlugin::startCommit() // Keep the file alive, else it removes self and forgets // its name changeTmpFile.close(); - openSubmitEditor(m_commitMessageFileName, data); + openSubmitEditor(m_commitMessageFileName, data, amend); } -Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd) +Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend) { Core::IEditor *editor = m_core->editorManager()->openEditor(fileName, QLatin1String(Constants::GITSUBMITEDITOR_ID)); if (Git::Constants::debug) @@ -667,6 +683,10 @@ Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const Commit submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentAction, m_diffSelectedFilesAction); submitEditor->setCommitData(cd); submitEditor->setCheckScriptWorkingDirectory(m_submitRepository); + const QString title = amend ? tr("Amend %1").arg(cd.amendSHA1) : tr("Git Commit"); + submitEditor->setDisplayName(title); + if (amend) // Allow for just correcting the message + submitEditor->setEmptyFileListEnabled(true); connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList))); return editor; } @@ -721,7 +741,7 @@ bool GitPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEdi if (Git::Constants::debug) qDebug() << Q_FUNC_INFO << fileList; bool closeEditor = true; - if (!fileList.empty()) { + if (!fileList.empty() || !m_commitAmendSHA1.isEmpty()) { // get message & commit m_core->fileManager()->blockFileChange(fileIFace); fileIFace->save(); @@ -729,6 +749,7 @@ bool GitPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEdi closeEditor = m_gitClient->addAndCommit(m_submitRepository, editor->panelData(), + m_commitAmendSHA1, m_commitMessageFileName, fileList, m_submitOrigCommitFiles, diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h index 85a4f967590..cec2821bd72 100644 --- a/src/plugins/git/gitplugin.h +++ b/src/plugins/git/gitplugin.h @@ -122,6 +122,7 @@ private slots: void showCommit(); void startCommit(); + void startAmendCommit(); void stash(); void stashSnapshot(); void branchList(); @@ -170,10 +171,11 @@ private: bool addToLocator, GitClientMemberFunc); bool isCommitEditorOpen() const; - Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd); + Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend); void cleanCommitMessageFile(); void cleanRepository(const QString &directory); void applyPatch(const QString &workingDirectory, QString file = QString()); + void startCommit(bool amend); static GitPlugin *m_instance; Core::ICore *m_core; @@ -201,6 +203,7 @@ private: QStringList m_submitOrigCommitFiles; QStringList m_submitOrigDeleteFiles; QString m_commitMessageFileName; + QString m_commitAmendSHA1; bool m_submitActionTriggered; }; diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp index 5aa11347bf4..8452142e7e4 100644 --- a/src/plugins/git/gitsubmiteditor.cpp +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -52,7 +52,6 @@ GitSubmitEditor::GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *p VCSBaseSubmitEditor(parameters, new GitSubmitEditorWidget(parent)), m_model(0) { - setDisplayName(tr("Git Commit")); connect(this, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(slotDiffSelected(QStringList))); } diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp index ca20460aa3b..736a7981685 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.cpp +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.cpp @@ -230,6 +230,7 @@ void VCSBaseSubmitEditor::unregisterActions(QAction *editorUndoAction, QAction m_d->m_widget->unregisterActions(editorUndoAction, editorRedoAction, submitAction, diffAction); m_d->m_diffAction = m_d->m_submitAction = 0; } + int VCSBaseSubmitEditor::fileNameColumn() const { return m_d->m_widget->fileNameColumn(); @@ -250,6 +251,16 @@ void VCSBaseSubmitEditor::setFileListSelectionMode(QAbstractItemView::SelectionM m_d->m_widget->setFileListSelectionMode(sm); } +bool VCSBaseSubmitEditor::isEmptyFileListEnabled() const +{ + return m_d->m_widget->isEmptyFileListEnabled(); +} + +void VCSBaseSubmitEditor::setEmptyFileListEnabled(bool e) +{ + m_d->m_widget->setEmptyFileListEnabled(e); +} + bool VCSBaseSubmitEditor::lineWrap() const { return m_d->m_widget->lineWrap(); diff --git a/src/plugins/vcsbase/vcsbasesubmiteditor.h b/src/plugins/vcsbase/vcsbasesubmiteditor.h index 414aa4bdb14..2880043c533 100644 --- a/src/plugins/vcsbase/vcsbasesubmiteditor.h +++ b/src/plugins/vcsbase/vcsbasesubmiteditor.h @@ -90,6 +90,7 @@ class VCSBASE_EXPORT VCSBaseSubmitEditor : public Core::IEditor Q_PROPERTY(bool lineWrap READ lineWrap WRITE setLineWrap DESIGNABLE true) Q_PROPERTY(int lineWrapWidth READ lineWrapWidth WRITE setLineWrapWidth DESIGNABLE true) Q_PROPERTY(QString checkScriptWorkingDirectory READ checkScriptWorkingDirectory WRITE setCheckScriptWorkingDirectory DESIGNABLE true) + Q_PROPERTY(bool emptyFileListEnabled READ isEmptyFileListEnabled WRITE setEmptyFileListEnabled DESIGNABLE true) protected: explicit VCSBaseSubmitEditor(const VCSBaseSubmitEditorParameters *parameters, @@ -122,6 +123,10 @@ public: QAbstractItemView::SelectionMode fileListSelectionMode() const; void setFileListSelectionMode(QAbstractItemView::SelectionMode sm); + // 'Commit' action enabled despite empty file list + bool isEmptyFileListEnabled() const; + void setEmptyFileListEnabled(bool e); + bool lineWrap() const; void setLineWrap(bool); -- GitLab