summaryrefslogtreecommitdiff
path: root/src/plugins/git/gitclient.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/git/gitclient.cpp')
-rw-r--r--src/plugins/git/gitclient.cpp423
1 files changed, 360 insertions, 63 deletions
diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp
index 7ed91ff072..134ab12030 100644
--- a/src/plugins/git/gitclient.cpp
+++ b/src/plugins/git/gitclient.cpp
@@ -56,6 +56,10 @@
#include <vcsbase/vcsbaseoutputwindow.h>
#include <vcsbase/vcsbaseplugin.h>
+#include <diffeditor/diffeditorwidget.h>
+#include <diffeditor/diffeditoreditable.h>
+#include <diffeditor/diffeditorconstants.h>
+
#include <QCoreApplication>
#include <QDir>
#include <QFileInfo>
@@ -75,6 +79,224 @@ static const char graphLogFormatC[] = "%h %d %an %s %ci";
namespace Git {
namespace Internal {
+class GitDiffHandler : public QObject
+{
+ Q_OBJECT
+
+public:
+ GitDiffHandler(const QString &gitPath,
+ const QString &workingDirectory,
+ const QProcessEnvironment &environment,
+ DiffEditor::DiffEditorWidget *editor,
+ int timeout);
+
+ // index -> working tree
+ void diffFile(const QString &fileName);
+ // stagedFileNames - files in index, diff will compare the state in HEAD to the one in the index
+ // unstagedFileNames - diff will compare the state in the index to the one in the working tree
+ void diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames);
+ // index -> working tree
+ void diffProjects(const QStringList &projectPaths);
+ // index -> working tree
+ void diffRepository();
+
+private slots:
+ void slotFileListReceived(const QByteArray &data);
+ void slotFileContentsReceived(const QByteArray &data);
+
+private:
+ void collectFilesList(const QStringList &additionalArguments);
+ void collectFilesContents();
+ void feedEditor();
+ QString workingTreeContents(const QString &fileName) const;
+
+ const QString m_gitPath;
+ const QString m_workingDirectory;
+ const QProcessEnvironment m_processEnvironment;
+ QWeakPointer<DiffEditor::DiffEditorWidget> m_editor;
+ const int m_timeout;
+ const QString m_waitMessage;
+
+ enum RevisionType {
+ Head = 0x01,
+ Index = 0x02,
+ WorkingTree = 0x04
+ };
+
+ QStringList m_requestedHeadFileNames;
+ QStringList m_requestedIndexFileNames;
+
+ QStringList m_headFileNames;
+ QStringList m_indexFileNames;
+
+ QStringList m_headContents;
+ QStringList m_indexContents;
+};
+
+GitDiffHandler::GitDiffHandler(const QString &gitPath,
+ const QString &workingDirectory,
+ const QProcessEnvironment &environment,
+ DiffEditor::DiffEditorWidget *editor,
+ int timeout)
+ : m_gitPath(gitPath),
+ m_workingDirectory(workingDirectory),
+ m_processEnvironment(environment),
+ m_editor(editor),
+ m_timeout(timeout),
+ m_waitMessage(tr("Waiting for data..."))
+{
+}
+
+void GitDiffHandler::diffFile(const QString &fileName)
+{
+ m_requestedIndexFileNames << fileName;
+ collectFilesList(QStringList() << QLatin1String("--") << m_requestedIndexFileNames);
+}
+
+void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
+{
+ m_requestedHeadFileNames = stagedFileNames;
+ m_requestedHeadFileNames.removeDuplicates();
+ m_requestedIndexFileNames = unstagedFileNames;
+ m_requestedIndexFileNames.removeDuplicates();
+
+ m_headFileNames = m_requestedHeadFileNames;
+ m_indexFileNames = m_requestedIndexFileNames;
+ for (int i = 0; i < m_headFileNames.count(); i++) {
+ const QString headFileName = m_headFileNames.at(i);
+ if (!m_indexFileNames.contains(headFileName))
+ m_indexFileNames.append(headFileName);
+ }
+ collectFilesContents();
+}
+
+void GitDiffHandler::diffProjects(const QStringList &projectPaths)
+{
+ collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
+}
+
+void GitDiffHandler::diffRepository()
+{
+ collectFilesList(QStringList());
+}
+
+void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
+{
+ m_editor.data()->clear(m_waitMessage);
+ VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+ connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileListReceived(QByteArray)));
+ QStringList arguments;
+ arguments << QLatin1String("diff") << QLatin1String("--name-only") << additionalArguments;
+ command->addJob(arguments, m_timeout);
+ command->execute();
+}
+
+void GitDiffHandler::slotFileListReceived(const QByteArray &data)
+{
+ if (m_editor.isNull())
+ return;
+
+ const QString fileList = m_editor.data()->codec()->toUnicode(data);
+ m_requestedIndexFileNames = fileList.split(QLatin1Char('\n'), QString::SkipEmptyParts);
+ m_requestedIndexFileNames.removeDuplicates();
+ m_indexFileNames = m_requestedIndexFileNames;
+
+ collectFilesContents();
+}
+
+void GitDiffHandler::collectFilesContents()
+{
+ const int headFilesReceived = m_headContents.count();
+ const int indexFilesReceived = m_indexContents.count();
+
+ if (headFilesReceived < m_headFileNames.count()) {
+ VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+ connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
+
+ QStringList arguments;
+ arguments << QLatin1String("show") << QLatin1String("HEAD:./") + m_headFileNames.at(headFilesReceived);
+ command->addJob(arguments, m_timeout);
+ command->execute();
+ } else if (indexFilesReceived < m_indexFileNames.count()) {
+ VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
+ connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
+
+ QStringList arguments;
+ arguments << QLatin1String("show") << QLatin1String(":./") + m_indexFileNames.at(indexFilesReceived);
+ command->addJob(arguments, m_timeout);
+ command->execute();
+ } else {
+ feedEditor();
+ }
+}
+
+void GitDiffHandler::slotFileContentsReceived(const QByteArray &data)
+{
+ if (m_editor.isNull())
+ return;
+
+ const int headFilesReceived = m_headContents.count();
+ const int indexFilesReceived = m_indexContents.count();
+ const QString contents = m_editor.data()->codec()->toUnicode(data);
+ if (headFilesReceived < m_headFileNames.count())
+ m_headContents.append(contents);
+ else if (indexFilesReceived < m_indexFileNames.count())
+ m_indexContents.append(contents);
+
+ collectFilesContents();
+}
+
+void GitDiffHandler::feedEditor()
+{
+ QList<DiffEditor::DiffEditorWidget::DiffFilesContents> list;
+
+ for (int i = 0; i < m_requestedHeadFileNames.count(); i++) {
+ const QString fileName = m_requestedHeadFileNames.at(i);
+ const QString original = m_headContents.at(i);
+ const int idx = m_indexFileNames.indexOf(fileName);
+ if (idx >= 0) {
+ const QString modified = m_indexContents.at(idx);
+ if (original != modified) {
+ DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
+ dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Head"));
+ dfc.leftText = original;
+ dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
+ dfc.rightText = modified;
+ list.append(dfc);
+ }
+ }
+ }
+ for (int i = 0; i < m_requestedIndexFileNames.count(); i++) {
+ const QString fileName = m_requestedIndexFileNames.at(i);
+ const QString original = m_indexContents.at(i);
+ const QString modified = workingTreeContents(fileName);
+ if (original != modified) {
+ DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
+ dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Index"));
+ dfc.leftText = original;
+ dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, tr("Working tree"));
+ dfc.rightText = modified;
+ list.append(dfc);
+ }
+ }
+ m_editor.data()->setDiff(list, m_workingDirectory);
+ deleteLater();
+}
+
+QString GitDiffHandler::workingTreeContents(const QString &fileName) const
+{
+ QDir workingDir(m_workingDirectory);
+ QString absoluteFileName = workingDir.absoluteFilePath(fileName);
+
+ QFile file(absoluteFileName);
+ if (file.open(QIODevice::ReadOnly | QIODevice::Text)) {
+ return m_editor.data()->codec()->toUnicode(file.readAll());
+ }
+ return QString();
+}
+
+///////////////////////////////////////////////////////////
+
class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
{
Q_OBJECT
@@ -506,6 +728,22 @@ VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *regis
return rc;
}
+DiffEditor::DiffEditorWidget *GitClient::findExistingDiffEditor(const char *registerDynamicProperty,
+ const QString &dynamicPropertyValue) const
+{
+ DiffEditor::DiffEditorWidget *editorWidget = 0;
+ Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
+ if (!outputEditor)
+ return 0;
+
+ // Exists already
+ Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
+ outputEditor->createNew(m_msgWait);
+ editorWidget = diffEditorWidget(outputEditor);
+
+ return editorWidget;
+}
+
/* Create an editor associated to VCS output of a source file/directory
* (using the file's codec). Makes use of a dynamic property to find an
* existing instance and to reuse it (in case, say, 'git diff foo' is
@@ -549,94 +787,153 @@ VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(const Core::Id &id,
return rc;
}
+DiffEditor::DiffEditorWidget *GitClient::diffEditorWidget(const Core::IEditor *editor) const
+{
+ if (const DiffEditor::DiffEditorEditable *de = qobject_cast<const DiffEditor::DiffEditorEditable *>(editor))
+ return de->editorWidget();
+ return 0;
+}
+
void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs,
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames)
{
- const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
- const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
- const QString title = tr("Git Diff");
-
- VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory);
- if (!editor) {
- GitCommitDiffArgumentsWidget *argWidget =
- new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs,
- unstagedFileNames, stagedFileNames);
+ if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
+ const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
+ QString title = tr("Git Diff");
- editor = createVcsEditor(editorId, title,
- workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget);
- connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
- }
-
- GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget());
- QStringList userDiffArgs = argWidget->arguments();
- editor->setDiffBaseDirectory(workingDirectory);
+ DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", workingDirectory);
- // Create a batch of 2 commands to be run after each other in case
- // we have a mixture of staged/unstaged files as is the case
- // when using the submit dialog.
- VcsBase::Command *command = createCommand(workingDirectory, editor);
- // Directory diff?
+ if (!editorWidget) {
+ Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
+ outputEditor->document()->setProperty("originalFileName", workingDirectory);
+ Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch); // should probably go outside this block
- QStringList cmdArgs;
- cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption);
+ editorWidget = diffEditorWidget(outputEditor);
+ }
- int timeout = settings()->intValue(GitSettings::timeoutKey);
+ int timeout = settings()->intValue(GitSettings::timeoutKey);
+ GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout);
- if (unstagedFileNames.empty() && stagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
+ if (unstagedFileNames.empty() && stagedFileNames.empty()) {
+ // local repository diff
+ handler->diffRepository();
+ } else {
+ if (!stagedFileNames.empty()) {
+ // diff of selected files only with --cached option, used in commit editor
+ handler->diffFiles(stagedFileNames, unstagedFileNames);
+ } else if (!unstagedFileNames.empty()) {
+ // current project diff
+ handler->diffProjects(unstagedFileNames);
+ }
+ }
} else {
- // Files diff.
- if (!unstagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs;
- arguments << QLatin1String("--") << unstagedFileNames;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
+ const QString binary = settings()->stringValue(GitSettings::binaryPathKey);
+ const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
+ const QString title = tr("Git Diff");
+
+ VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", workingDirectory);
+ if (!editor) {
+ GitCommitDiffArgumentsWidget *argWidget =
+ new GitCommitDiffArgumentsWidget(this, workingDirectory, diffArgs,
+ unstagedFileNames, stagedFileNames);
+
+ editor = createVcsEditor(editorId, title,
+ workingDirectory, CodecSource, "originalFileName", workingDirectory, argWidget);
+ connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
}
- if (!stagedFileNames.empty()) {
- QStringList arguments(cmdArgs);
- arguments << userDiffArgs;
- arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames;
- outputWindow()->appendCommand(workingDirectory, binary, arguments);
- command->addJob(arguments, timeout);
+
+ GitCommitDiffArgumentsWidget *argWidget = qobject_cast<GitCommitDiffArgumentsWidget *>(editor->configurationWidget());
+ QStringList userDiffArgs = argWidget->arguments();
+ editor->setDiffBaseDirectory(workingDirectory);
+
+ // Create a batch of 2 commands to be run after each other in case
+ // we have a mixture of staged/unstaged files as is the case
+ // when using the submit dialog.
+ VcsBase::Command *command = createCommand(workingDirectory, editor);
+ // Directory diff?
+
+ QStringList cmdArgs;
+ cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption);
+
+ int timeout = settings()->intValue(GitSettings::timeoutKey);
+
+ if (unstagedFileNames.empty() && stagedFileNames.empty()) {
+ QStringList arguments(cmdArgs);
+ arguments << userDiffArgs;
+ outputWindow()->appendCommand(workingDirectory, binary, arguments);
+ command->addJob(arguments, timeout);
+ } else {
+ // Files diff.
+ if (!unstagedFileNames.empty()) {
+ QStringList arguments(cmdArgs);
+ arguments << userDiffArgs;
+ arguments << QLatin1String("--") << unstagedFileNames;
+ outputWindow()->appendCommand(workingDirectory, binary, arguments);
+ command->addJob(arguments, timeout);
+ }
+ if (!stagedFileNames.empty()) {
+ QStringList arguments(cmdArgs);
+ arguments << userDiffArgs;
+ arguments << QLatin1String("--cached") << diffArgs << QLatin1String("--") << stagedFileNames;
+ outputWindow()->appendCommand(workingDirectory, binary, arguments);
+ command->addJob(arguments, timeout);
+ }
}
+ command->execute();
}
- command->execute();
}
void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs,
const QString &fileName)
{
- const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
- const QString title = tr("Git Diff \"%1\"").arg(fileName);
- const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
+ if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
+ const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
+ QString title = tr("Git Diff \"%1\"").arg(fileName);
+ const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
- VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile);
- if (!editor) {
- GitFileDiffArgumentsWidget *argWidget =
- new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, fileName);
+ DiffEditor::DiffEditorWidget *editorWidget = findExistingDiffEditor("originalFileName", sourceFile);
+ if (!editorWidget) {
+ Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait);
+ outputEditor->document()->setProperty("originalFileName", sourceFile);
+ Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
- editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget);
- connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
- }
- editor->setDiffBaseDirectory(workingDirectory);
+ editorWidget = diffEditorWidget(outputEditor);
+ }
- GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget());
- QStringList userDiffArgs = argWidget->arguments();
+ if (!fileName.isEmpty()) {
+ int timeout = settings()->intValue(GitSettings::timeoutKey);
+ GitDiffHandler *handler = new GitDiffHandler(gitBinaryPath(), workingDirectory, processEnvironment(), editorWidget, timeout);
+ handler->diffFile(fileName);
+ }
+ } else {
+ const Core::Id editorId = Git::Constants::GIT_DIFF_EDITOR_ID;
+ const QString title = tr("Git Diff \"%1\"").arg(fileName);
+ const QString sourceFile = VcsBase::VcsBaseEditorWidget::getSource(workingDirectory, fileName);
- QStringList cmdArgs;
- cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
- << userDiffArgs;
+ VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile);
+ if (!editor) {
+ GitFileDiffArgumentsWidget *argWidget =
+ new GitFileDiffArgumentsWidget(this, workingDirectory, diffArgs, fileName);
- if (!fileName.isEmpty())
- cmdArgs << QLatin1String("--") << fileName;
- executeGit(workingDirectory, cmdArgs, editor);
+ editor = createVcsEditor(editorId, title, sourceFile, CodecSource, "originalFileName", sourceFile, argWidget);
+ connect(editor, SIGNAL(diffChunkReverted(VcsBase::DiffChunk)), argWidget, SLOT(executeCommand()));
+ }
+ editor->setDiffBaseDirectory(workingDirectory);
+
+ GitFileDiffArgumentsWidget *argWidget = qobject_cast<GitFileDiffArgumentsWidget *>(editor->configurationWidget());
+ QStringList userDiffArgs = argWidget->arguments();
+
+ QStringList cmdArgs;
+ cmdArgs << QLatin1String("diff") << QLatin1String(noColorOption)
+ << userDiffArgs;
+
+ if (!fileName.isEmpty())
+ cmdArgs << QLatin1String("--") << fileName;
+ executeGit(workingDirectory, cmdArgs, editor);
+ }
}
void GitClient::diffBranch(const QString &workingDirectory,