Commit 367cfc84 authored by Francois Ferrand's avatar Francois Ferrand Committed by Orgad Shaneh
Browse files

Git: Support staging a single chunk.



Add context menu in diff editor to stage/unstage a single chunk from the diff.

Task-number: QTCREATORBUG-5875
Change-Id: Ic244a0d84b5ed5f66b90d7fe8784fc1b8041d183
Reviewed-by: Orgad Shaneh's avatarOrgad Shaneh <orgads@gmail.com>
Reviewed-by: default avatarTobias Hunger <tobias.hunger@digia.com>
parent c372f7b6
......@@ -1115,6 +1115,8 @@ void GitClient::diff(const QString &workingDirectory,
workingDirectory,
argWidget);
newEditor = vcsEditor->editor();
connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
}
......@@ -1222,6 +1224,8 @@ void GitClient::diff(const QString &workingDirectory,
sourceFile,
argWidget);
newEditor = vcsEditor->editor();
connect(vcsEditor, SIGNAL(diffChunkApplied(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
connect(vcsEditor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)),
argWidget, SLOT(executeCommand()));
}
......@@ -2433,10 +2437,11 @@ bool GitClient::synchronousCleanList(const QString &workingDirectory, QStringLis
}
bool GitClient::synchronousApplyPatch(const QString &workingDirectory,
const QString &file, QString *errorMessage)
const QString &file, QString *errorMessage,
const QStringList &arguments)
{
QStringList args;
args << QLatin1String("apply") << QLatin1String("--whitespace=fix") << file;
args << QLatin1String("apply") << QLatin1String("--whitespace=fix") << arguments << file;
QByteArray outputText;
QByteArray errorText;
const bool rc = fullySynchronousGit(workingDirectory, args, &outputText, &errorText);
......
......@@ -182,7 +182,7 @@ public:
const QStringList &files = QStringList(),
QString *errorMessage = 0);
bool synchronousCleanList(const QString &workingDirectory, QStringList *files, QStringList *ignoredFiles, QString *errorMessage);
bool synchronousApplyPatch(const QString &workingDirectory, const QString &file, QString *errorMessage);
bool synchronousApplyPatch(const QString &workingDirectory, const QString &file, QString *errorMessage, const QStringList &arguments = QStringList());
bool synchronousInit(const QString &workingDirectory);
bool synchronousCheckoutFiles(const QString &workingDirectory,
QStringList files = QStringList(),
......
......@@ -44,9 +44,12 @@
#include <QFileInfo>
#include <QRegExp>
#include <QSet>
#include <QTemporaryFile>
#include <QDir>
#include <QTextCursor>
#include <QTextBlock>
#include <QMessageBox>
#define CHANGE_PATTERN "[a-f0-9]{7,40}"
......@@ -223,6 +226,53 @@ void GitEditor::revertChange()
GitPlugin::instance()->gitClient()->synchronousRevert(workingDirectory, m_currentChange);
}
void GitEditor::stageDiffChunk()
{
const QAction *a = qobject_cast<QAction *>(sender());
QTC_ASSERT(a, return);
const VcsBase::DiffChunk chunk = qvariant_cast<VcsBase::DiffChunk>(a->data());
return applyDiffChunk(chunk, false);
}
void GitEditor::unstageDiffChunk()
{
const QAction *a = qobject_cast<QAction *>(sender());
QTC_ASSERT(a, return);
const VcsBase::DiffChunk chunk = qvariant_cast<VcsBase::DiffChunk>(a->data());
return applyDiffChunk(chunk, true);
}
void GitEditor::applyDiffChunk(const VcsBase::DiffChunk& chunk, bool revert)
{
VcsBase::VcsBaseOutputWindow *outwin = VcsBase::VcsBaseOutputWindow::instance();
QTemporaryFile patchFile;
if (!patchFile.open())
return;
const QString baseDir = diffBaseDirectory();
patchFile.write(chunk.header);
patchFile.write(chunk.chunk);
patchFile.close();
GitClient *client = GitPlugin::instance()->gitClient();
QStringList args = QStringList() << QLatin1String("--cached");
if (revert)
args << QLatin1String("--reverse");
QString errorMessage;
if (client->synchronousApplyPatch(baseDir, patchFile.fileName(), &errorMessage, args)) {
if (errorMessage.isEmpty())
outwin->append(tr("Chunk successfully staged"));
else
outwin->append(errorMessage);
if (revert)
emit diffChunkReverted(chunk);
else
emit diffChunkApplied(chunk);
} else {
outwin->appendError(errorMessage);
}
}
void GitEditor::init()
{
VcsBase::VcsBaseEditorWidget::init();
......@@ -233,6 +283,19 @@ void GitEditor::init()
new GitRebaseHighlighter(baseTextDocument().data());
}
void GitEditor::addDiffActions(QMenu *menu, const VcsBase::DiffChunk &chunk)
{
menu->addSeparator();
QAction *stageAction = menu->addAction(tr("Stage Chunk..."));
stageAction->setData(qVariantFromValue(chunk));
connect(stageAction, SIGNAL(triggered()), this, SLOT(stageDiffChunk()));
QAction *unstageAction = menu->addAction(tr("Unstage Chunk..."));
unstageAction->setData(qVariantFromValue(chunk));
connect(unstageAction, SIGNAL(triggered()), this, SLOT(unstageDiffChunk()));
}
bool GitEditor::open(QString *errorString, const QString &fileName, const QString &realFileName)
{
bool res = VcsBaseEditorWidget::open(errorString, fileName, realFileName);
......
......@@ -57,9 +57,13 @@ public slots:
private slots:
void cherryPickChange();
void revertChange();
void stageDiffChunk();
void unstageDiffChunk();
void applyDiffChunk(const VcsBase::DiffChunk& chunk, bool revert);
private:
void init();
void addDiffActions(QMenu *menu, const VcsBase::DiffChunk &chunk);
bool open(QString *errorString, const QString &fileName, const QString &realFileName);
QSet<QString> annotationChanges() const;
QString changeUnderCursor(const QTextCursor &) const;
......
......@@ -950,6 +950,8 @@ void VcsBaseEditorWidget::contextMenuEvent(QContextMenuEvent *e)
QAction *revertAction = menu->addAction(tr("Revert Chunk..."));
revertAction->setData(qVariantFromValue(Internal::DiffChunkAction(chunk, true)));
connect(revertAction, SIGNAL(triggered()), this, SLOT(slotApplyDiffChunk()));
// Custom diff actions
addDiffActions(menu, chunk);
break;
}
default:
......@@ -1145,7 +1147,8 @@ DiffChunk VcsBaseEditorWidget::diffChunk(QTextCursor cursor) const
}
if (!chunkStart || !block.isValid())
return rc;
rc.fileName = findDiffFile(fileNameFromDiffSpecification(block));
QString header;
rc.fileName = findDiffFile(fileNameFromDiffSpecification(block, &header));
if (rc.fileName.isEmpty())
return rc;
// Concatenate chunk and convert
......@@ -1163,6 +1166,7 @@ DiffChunk VcsBaseEditorWidget::diffChunk(QTextCursor cursor) const
}
const QTextCodec *cd = baseTextDocument()->codec();
rc.chunk = cd ? cd->fromUnicode(unicode) : unicode.toLocal8Bit();
rc.header = cd ? cd->fromUnicode(header) : header.toLocal8Bit();
return rc;
}
......@@ -1388,6 +1392,10 @@ QString VcsBaseEditorWidget::findDiffFile(const QString &f) const
return QString();
}
void VcsBaseEditorWidget::addDiffActions(QMenu *, const DiffChunk &)
{
}
void VcsBaseEditorWidget::slotAnnotateRevision()
{
if (const QAction *a = qobject_cast<const QAction *>(sender()))
......@@ -1430,18 +1438,25 @@ bool VcsBaseEditorWidget::applyDiffChunk(const DiffChunk &dc, bool revert) const
d->m_diffBaseDirectory, 0, revert);
}
QString VcsBaseEditorWidget::fileNameFromDiffSpecification(const QTextBlock &inBlock) const
QString VcsBaseEditorWidget::fileNameFromDiffSpecification(const QTextBlock &inBlock, QString *header) const
{
// Go back chunks
QString fileName;
for (QTextBlock block = inBlock; block.isValid(); block = block.previous()) {
const QString line = block.text();
if (d->m_diffFilePattern.indexIn(line) != -1) {
QString cap = d->m_diffFilePattern.cap(1);
if (!cap.isEmpty())
return findDiffFile(cap);
if (header)
header->prepend(line + QLatin1String("\n"));
if (fileName.isEmpty() && !cap.isEmpty())
fileName = cap;
} else if (!fileName.isEmpty()) {
return findDiffFile(fileName);
} else if (header) {
header->clear();
}
}
return QString();
return fileName.isEmpty() ? QString() : findDiffFile(fileName);
}
void VcsBaseEditorWidget::addChangeActions(QMenu *, const QString &)
......
......@@ -83,6 +83,7 @@ public:
QString fileName;
QByteArray chunk;
QByteArray header;
};
class VCSBASE_EXPORT VcsBaseEditorWidget : public TextEditor::BaseTextEditorWidget
......@@ -234,6 +235,8 @@ protected:
* source and version control. */
virtual QString findDiffFile(const QString &f) const;
virtual void addDiffActions(QMenu *menu, const DiffChunk &chunk);
virtual void addChangeActions(QMenu *menu, const QString &change);
// Implement to return a set of change identifiers in
......@@ -245,7 +248,7 @@ protected:
virtual BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const = 0;
// Returns a local file name from the diff file specification
// (text cursor at position above change hunk)
QString fileNameFromDiffSpecification(const QTextBlock &inBlock) const;
QString fileNameFromDiffSpecification(const QTextBlock &inBlock, QString *header = 0) const;
// Implement to return decorated annotation change for "Annotate version"
virtual QString decorateVersion(const QString &revision) const;
......
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