Newer
Older
/****************************************************************************
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia. For licensing terms and
** conditions see http://qt.digia.com/licensing. For further information
** use the contact form at http://qt.digia.com/contact-us.
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights. These rights are described in the Digia Qt LGPL Exception
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
****************************************************************************/
#include "gitconstants.h"
#include "gitplugin.h"
#include "gitsubmiteditor.h"
#include "gitversioncontrol.h"
#include <vcsbase/submitfilemodel.h>
#include <coreplugin/vcsmanager.h>
#include <coreplugin/id.h>
#include <coreplugin/iversioncontrol.h>
#include <utils/synchronousprocess.h>
#include <vcsbase/vcsbaseeditorparameterwidget.h>
#include <vcsbase/vcsbaseoutputwindow.h>
#include <vcsbase/vcsbaseplugin.h>
#include <diffeditor/diffeditorwidget.h>
#include <diffeditor/diffeditoreditable.h>
#include <diffeditor/diffeditorconstants.h>
#include <QSignalMapper>
#include <QToolButton>
#include <QTextCodec>
static const char GIT_DIRECTORY[] = ".git";
static const char graphLogFormatC[] = "%h %d %an %s %ci";
static const char HEAD[] = "HEAD";
namespace Git {
namespace Internal {
class GitDiffHandler : public QObject
{
Q_OBJECT
public:
enum RevisionType {
WorkingTree,
Index,
Other
};
struct Revision {
Revision() : type(WorkingTree) { }
Revision(RevisionType t) : type(t) { }
Revision(RevisionType t, const QString &i) : type(t), id(i) { }
RevisionType type;
QString id; // can be sha or HEAD
QString infoText() const
{
switch (type) {
case WorkingTree: return tr("Working tree");
case Index: return tr("Index");
default: return id;
}
}
};
GitDiffHandler(DiffEditor::DiffEditorEditable *editor,
const QString &gitPath,
const QString &workingDirectory,
const QProcessEnvironment &environment,
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();
// branch HEAD -> working tree
void diffBranch(const QString &branchName);
// id^ -> id
void show(const QString &id);
private slots:
void slotFileListReceived(const QByteArray &data);
void slotFileContentsReceived(const QByteArray &data);
private:
void collectFilesList(const QStringList &additionalArguments);
void prepareForCollection();
void collectFilesContents();
void feedEditor();
QString workingTreeContents(const QString &fileName) const;
QPointer<DiffEditor::DiffEditorEditable> m_editor;
const QString m_gitPath;
const QString m_workingDirectory;
const QProcessEnvironment m_processEnvironment;
const int m_timeout;
const QString m_waitMessage;
struct RevisionRange {
RevisionRange() { }
RevisionRange(const Revision &b, const Revision &e) : begin(b), end(e) { }
Revision begin;
Revision end;
// filename, revision range
QMap<QString, QList<RevisionRange> > m_requestedRevisionRanges;
// filename, revision, dummy
QMap<QString, QMap<Revision, bool> > m_pendingRevisions;
// filename, revision, contents
QMap<QString, QMap<Revision, QString> > m_collectedRevisions;
RevisionRange m_requestedRevisionRange;
inline bool operator<(const GitDiffHandler::Revision &rev1, const GitDiffHandler::Revision &rev2)
{
if (rev1.type != rev2.type)
return rev1.type < rev2.type;
return rev1.id < rev2.id;
}
GitDiffHandler::GitDiffHandler(DiffEditor::DiffEditorEditable *editor,
const QString &gitPath,
const QString &workingDirectory,
const QProcessEnvironment &environment,
int timeout)
: m_editor(editor),
m_gitPath(gitPath),
m_workingDirectory(workingDirectory),
m_processEnvironment(environment),
m_timeout(timeout),
m_waitMessage(tr("Waiting for data..."))
{
}
void GitDiffHandler::diffFile(const QString &fileName)
{
m_requestedRevisionRange = RevisionRange(
Revision(Other, QLatin1String(HEAD)),
Revision(WorkingTree));
collectFilesList(QStringList() << QLatin1String("--") << fileName);
}
void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
{
RevisionRange stagedRange = RevisionRange(
Revision(Other, QLatin1String(HEAD)),
Revision(WorkingTree));
RevisionRange unstagedRange = RevisionRange(
Revision(Index),
Revision(WorkingTree));
for (int i = 0; i < stagedFileNames.count(); i++)
m_requestedRevisionRanges[stagedFileNames.at(i)].append(stagedRange);
for (int i = 0; i < unstagedFileNames.count(); i++)
m_requestedRevisionRanges[unstagedFileNames.at(i)].append(unstagedRange);
prepareForCollection();
collectFilesContents();
}
void GitDiffHandler::diffProjects(const QStringList &projectPaths)
{
m_requestedRevisionRange = RevisionRange(
Revision(Other, QLatin1String(HEAD)),
Revision(WorkingTree));
collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
}
void GitDiffHandler::diffRepository()
{
m_requestedRevisionRange = RevisionRange(
Revision(Other, QLatin1String(HEAD)),
Revision(WorkingTree));
collectFilesList(QStringList());
}
void GitDiffHandler::diffBranch(const QString &branchName)
{
m_requestedRevisionRange = RevisionRange(
Revision(Other, branchName),
Revision(WorkingTree));
collectFilesList(QStringList() << branchName);
}
void GitDiffHandler::show(const QString &id)
{
Revision begin(Other, id + QLatin1Char('^'));
Revision end(Other, id);
m_requestedRevisionRange = RevisionRange(begin, end);
collectFilesList(QStringList() << begin.id << end.id);
}
void GitDiffHandler::collectFilesList(const QStringList &additionalArguments)
{
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->editorWidget()->codec()->toUnicode(data);
QStringList fileNames = fileList.split(QLatin1Char('\n'), QString::SkipEmptyParts);
fileNames.removeDuplicates();
for (int i = 0; i < fileNames.count(); i++)
m_requestedRevisionRanges[fileNames.at(i)].append(m_requestedRevisionRange);
prepareForCollection();
collectFilesContents();
}
void GitDiffHandler::prepareForCollection()
QMap<QString, QList<RevisionRange> >::const_iterator it
= m_requestedRevisionRanges.constBegin();
QMap<QString, QList<RevisionRange> >::const_iterator itEnd
= m_requestedRevisionRanges.constEnd();
while (it != itEnd) {
const QString fileName = it.key();
const QList<RevisionRange> &ranges = it.value();
for (int i = 0; i < ranges.count(); i++) {
const RevisionRange &range = ranges.at(i);
m_pendingRevisions[fileName][range.begin] = false;
m_pendingRevisions[fileName][range.end] = false;
}
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
void GitDiffHandler::collectFilesContents()
{
QMap<QString, QMap<Revision, bool> >::iterator itFile
= m_pendingRevisions.begin();
QMap<QString, QMap<Revision, bool> >::iterator itFileEnd
= m_pendingRevisions.end();
while (itFile != itFileEnd) {
const QString fileName = itFile.key();
QMap<Revision, bool> &revisions = itFile.value();
QMap<Revision, bool>::iterator itRev
= revisions.begin();
QMap<Revision, bool>::iterator itRevEnd
= revisions.end();
while (itRev != itRevEnd) {
const Revision revision = itRev.key();
if (revision.type == WorkingTree) {
// collect file here
m_collectedRevisions[fileName][revision] = workingTreeContents(fileName);
itRev = revisions.erase(itRev); // iterate to the next revision
} else {
// prepare job here
VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotFileContentsReceived(QByteArray)));
QString revisionArgument = (revision.type == Other)
? revision.id : QString();
revisionArgument += QLatin1String(":./");
QStringList arguments;
arguments << QLatin1String("show") << revisionArgument + fileName;
command->addJob(arguments, m_timeout);
command->execute();
return;
}
}
itFile = m_pendingRevisions.erase(itFile); // iterate to the next file
}
void GitDiffHandler::slotFileContentsReceived(const QByteArray &data)
{
if (m_editor.isNull())
return;
QMap<QString, QMap<Revision, bool> >::iterator itFile
= m_pendingRevisions.begin();
QMap<QString, QMap<Revision, bool> >::iterator itFileEnd
= m_pendingRevisions.end();
if (itFile != itFileEnd) {
const QString fileName = itFile.key();
QMap<Revision, bool> &revisions = itFile.value();
QMap<Revision, bool>::iterator itRev
= revisions.begin();
QMap<Revision, bool>::iterator itRevEnd
= revisions.end();
if (itRev != itRevEnd) {
const QString contents = m_editor->editorWidget()->codec()->toUnicode(data);
m_collectedRevisions[fileName][itRev.key()] = contents;
itRev = revisions.erase(itRev);
if (revisions.isEmpty())
m_pendingRevisions.erase(itFile);
}
}
collectFilesContents();
}
void GitDiffHandler::feedEditor()
{
QList<DiffEditor::DiffEditorWidget::DiffFilesContents> list;
QMap<QString, QList<RevisionRange> >::const_iterator itFile
= m_requestedRevisionRanges.constBegin();
QMap<QString, QList<RevisionRange> >::const_iterator itFileEnd
= m_requestedRevisionRanges.constEnd();
while (itFile != itFileEnd) {
const QString fileName = itFile.key();
const QList<RevisionRange> &ranges = itFile.value();
for (int i = 0; i < ranges.count(); i++) {
const Revision leftRevision = ranges.at(i).begin;
const Revision rightRevision = ranges.at(i).end;
DiffEditor::DiffEditorWidget::DiffFilesContents dfc;
dfc.leftFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, leftRevision.infoText());
dfc.leftText = m_collectedRevisions[fileName][leftRevision];
dfc.rightFileInfo = DiffEditor::DiffEditorWidget::DiffFileInfo(fileName, rightRevision.infoText());
dfc.rightText = m_collectedRevisions[fileName][rightRevision];
m_editor->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->editorWidget()->codec()->toUnicode(file.readAll());
}
return QString();
}
///////////////////////////////////////////////////////////
class BaseGitDiffArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
BaseGitDiffArgumentsWidget(GitClient *client, const QString &directory,
QTC_ASSERT(!directory.isEmpty(), return);
QTC_ASSERT(m_client, return);
m_patienceButton = addToggleButton(QLatin1String("--patience"), tr("Patience"),
tr("Use the patience algorithm for calculating the differences."));
mapSetting(m_patienceButton, client->settings()->boolPointer(GitSettings::diffPatienceKey));
m_ignoreWSButton = addToggleButton(QLatin1String("--ignore-space-change"), tr("Ignore Whitespace"),
tr("Ignore whitespace only changes."));
mapSetting(m_ignoreWSButton, m_client->settings()->boolPointer(GitSettings::ignoreSpaceChangesInDiffKey));
QString m_workingDirectory;
GitClient *m_client;
QToolButton *m_patienceButton;
QToolButton *m_ignoreWSButton;
};
class GitCommitDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
{
GitCommitDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
const QStringList &args, const QStringList &unstaged,
const QStringList &staged) :
BaseGitDiffArgumentsWidget(client, directory, args),
m_unstagedFileNames(unstaged),
m_stagedFileNames(staged)
{ }
m_client->diff(m_workingDirectory, arguments(), m_unstagedFileNames, m_stagedFileNames);
}
private:
const QStringList m_unstagedFileNames;
const QStringList m_stagedFileNames;
};
class GitFileDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
{
GitFileDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
const QStringList &args, const QString &file) :
BaseGitDiffArgumentsWidget(client, directory, args),
m_client->diff(m_workingDirectory, arguments(), m_fileName);
}
private:
const QString m_fileName;
};
class GitBranchDiffArgumentsWidget : public BaseGitDiffArgumentsWidget
{
GitBranchDiffArgumentsWidget(Git::Internal::GitClient *client, const QString &directory,
const QStringList &args, const QString &branch) :
BaseGitDiffArgumentsWidget(client, directory, args),
m_client->diffBranch(m_workingDirectory, arguments(), m_branchName);
}
private:
const QString m_branchName;
};
class GitShowArgumentsWidget : public BaseGitDiffArgumentsWidget
GitShowArgumentsWidget(Git::Internal::GitClient *client,
const QString &directory,
const QStringList &args,
const QString &id) :
BaseGitDiffArgumentsWidget(client, directory, args),
m_client(client),
m_workingDirectory(directory),
QList<ComboBoxItem> prettyChoices;
prettyChoices << ComboBoxItem(tr("oneline"), QLatin1String("oneline"))
<< ComboBoxItem(tr("short"), QLatin1String("short"))
<< ComboBoxItem(tr("medium"), QLatin1String("medium"))
<< ComboBoxItem(tr("full"), QLatin1String("full"))
<< ComboBoxItem(tr("fuller"), QLatin1String("fuller"))
<< ComboBoxItem(tr("email"), QLatin1String("email"))
<< ComboBoxItem(tr("raw"), QLatin1String("raw"));
mapSetting(addComboBox(QStringList(QLatin1String("--pretty=%1")), prettyChoices),
m_client->settings()->intPointer(GitSettings::showPrettyFormatKey));
m_client->show(m_workingDirectory, m_id, arguments());
GitClient *m_client;
QString m_workingDirectory;
class GitBlameArgumentsWidget : public VcsBase::VcsBaseEditorParameterWidget
GitBlameArgumentsWidget(Git::Internal::GitClient *client,
const QString &directory,
const QStringList &args,
const QString &revision, const QString &fileName) :
m_client(client),
m_workingDirectory(directory),
m_revision(revision),
m_fileName(fileName)
{
mapSetting(addToggleButton(QString(), tr("Omit Date"),
tr("Hide the date of a change from the output.")),
m_client->settings()->boolPointer(GitSettings::omitAnnotationDateKey));
mapSetting(addToggleButton(QLatin1String("-w"), tr("Ignore Whitespace"),
tr("Ignore whitespace only changes.")),
m_client->settings()->boolPointer(GitSettings::ignoreSpaceChangesInBlameKey));
int line = -1;
if (m_editor)
line = m_editor->lineNumberOfCurrentEditor();
m_client->blame(m_workingDirectory, arguments(), m_fileName, m_revision, line);
GitClient *m_client;
QString m_workingDirectory;
QString m_revision;
QString m_fileName;
};
class GitLogArgumentsWidget : public BaseGitDiffArgumentsWidget
{
Q_OBJECT
public:
GitLogArgumentsWidget(Git::Internal::GitClient *client,
const QString &directory,
bool enableAnnotationContextMenu,
const QStringList &args,
const QStringList &fileNames) :
BaseGitDiffArgumentsWidget(client, directory, args),
m_client(client),
m_workingDirectory(directory),
m_enableAnnotationContextMenu(enableAnnotationContextMenu),
m_fileNames(fileNames)
{
QToolButton *diffButton = addToggleButton(QLatin1String("--patch"), tr("Show Diff"),
mapSetting(diffButton, m_client->settings()->boolPointer(GitSettings::logDiffKey));
connect(diffButton, SIGNAL(toggled(bool)), m_patienceButton, SLOT(setVisible(bool)));
connect(diffButton, SIGNAL(toggled(bool)), m_ignoreWSButton, SLOT(setVisible(bool)));
m_patienceButton->setVisible(diffButton->isChecked());
m_ignoreWSButton->setVisible(diffButton->isChecked());
QStringList graphArguments(QLatin1String("--graph"));
graphArguments << QLatin1String("--oneline") << QLatin1String("--topo-order");
graphArguments << (QLatin1String("--pretty=format:") + QLatin1String(graphLogFormatC));
QToolButton *graphButton = addToggleButton(graphArguments, tr("Graph"),
tr("Show textual graph log."));
mapSetting(graphButton, m_client->settings()->boolPointer(GitSettings::graphLogKey));
}
void executeCommand()
{
m_client->log(m_workingDirectory, m_fileNames, m_enableAnnotationContextMenu, arguments());
}
private:
GitClient *m_client;
QString m_workingDirectory;
bool m_enableAnnotationContextMenu;
QStringList m_fileNames;
};
class ConflictHandler : public QObject
{
Q_OBJECT
public:
ConflictHandler(VcsBase::Command *parentCommand,
const QString &workingDirectory,
const QString &command)
m_workingDirectory(workingDirectory),
m_command(command)
{
if (parentCommand) {
connect(parentCommand, SIGNAL(outputData(QByteArray)), this, SLOT(readStdOut(QByteArray)));
connect(parentCommand, SIGNAL(errorText(QString)), this, SLOT(readStdErr(QString)));
}
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(m_workingDirectory);
GitPlugin::instance()->gitClient()->endStashScope(m_workingDirectory);
} else {
GitPlugin::instance()->gitClient()->handleMergeConflicts(
m_workingDirectory, m_commit, m_command);
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
}
void readStdOutString(const QString &data)
{
static QRegExp patchFailedRE(QLatin1String("Patch failed at ([^\\n]*)"));
if (patchFailedRE.indexIn(data) != -1)
m_commit = patchFailedRE.cap(1);
}
public slots:
void readStdOut(const QByteArray &data)
{
readStdOutString(QString::fromUtf8(data));
}
void readStdErr(const QString &data)
{
static QRegExp couldNotApplyRE(QLatin1String("[Cc]ould not (?:apply|revert) ([^\\n]*)"));
if (couldNotApplyRE.indexIn(data) != -1)
m_commit = couldNotApplyRE.cap(1);
}
private:
QString m_workingDirectory;
QString m_command;
QString m_commit;
};
Core::IEditor *locateEditor(const char *property, const QString &entry)
foreach (Core::IEditor *ed, Core::ICore::editorManager()->openedEditors())
if (ed->document()->property(property).toString() == entry)
// Return converted command output, remove '\r' read on Windows
static inline QString commandOutputFromLocal8Bit(const QByteArray &a)
{
QString output = QString::fromLocal8Bit(a);
output.remove(QLatin1Char('\r'));
return output;
}
// Return converted command output split into lines
static inline QStringList commandOutputLinesFromLocal8Bit(const QByteArray &a)
{
QString output = commandOutputFromLocal8Bit(a);
const QChar newLine = QLatin1Char('\n');
if (output.endsWith(newLine))
output.truncate(output.size() - 1);
if (output.isEmpty())
return QStringList();
return output.split(newLine);
}
static inline QString msgRepositoryNotFound(const QString &dir)
{
return GitClient::tr("Cannot determine the repository for \"%1\".").arg(dir);
}
static inline QString msgParseFilesFailed()
{
return GitClient::tr("Cannot parse the file output.");
}
static inline QString currentDocumentPath()
{
if (Core::IEditor *editor = Core::EditorManager::currentEditor())
return QFileInfo(editor->document()->fileName()).path();
return QString();
// ---------------- GitClient
const char *GitClient::stashNamePrefix = "stash@{";
GitClient::GitClient(GitSettings *settings) :
m_cachedGitVersion(0),
m_repositoryChangedSignalMapper(0),
m_settings(settings),
m_disableEditor(false)
connect(Core::ICore::instance(), SIGNAL(saveSettingsRequested()), this, SLOT(saveSettings()));
m_gitQtcEditor = QString::fromLatin1("\"%1\" -client -block -pid %2")
.arg(QCoreApplication::applicationFilePath())
.arg(QCoreApplication::applicationPid());

Friedemann Kleint
committed
const char *GitClient::noColorOption = "--no-color";
const char *GitClient::decorateOption = "--decorate";

Friedemann Kleint
committed
QString GitClient::findRepositoryForDirectory(const QString &dir)
{
if (dir.endsWith(QLatin1String("/.git")) || dir.contains(QLatin1String("/.git/")))
return QString();
QString dotGit = QLatin1String(GIT_DIRECTORY);
// QFileInfo is outside loop, because it is faster this way
QFileInfo fileInfo;
if (directory.exists(dotGit)) {
fileInfo.setFile(directory, dotGit);
if (fileInfo.isFile())
return directory.absolutePath();
else if (directory.exists(QLatin1String(".git/config")))
return directory.absolutePath();
}
QString GitClient::findGitDirForRepository(const QString &repositoryDir) const
static QHash<QString, QString> repoDirCache;
QString &res = repoDirCache[repositoryDir];
if (!res.isEmpty())
return res;
QByteArray outputText;
QStringList arguments;
arguments << QLatin1String("rev-parse") << QLatin1String("--git-dir");
fullySynchronousGit(repositoryDir, arguments, &outputText, 0, false);
res = QString::fromLocal8Bit(outputText.trimmed());
if (!QDir(res).isAbsolute())
res.prepend(repositoryDir + QLatin1Char('/'));
return res;
QString GitClient::findRepositoryForGitDir(const QString &gitDir) const
{
static QHash<QString, QString> gitDirCache;
QString &res = gitDirCache[gitDir];
if (!res.isEmpty())
return res;
QByteArray outputText;
QStringList arguments;
arguments << QLatin1String("rev-parse") << QLatin1String("--show-toplevel");
fullySynchronousGit(gitDir, arguments, &outputText, 0, false);
return QString::fromLocal8Bit(outputText.trimmed());
}
VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *registerDynamicProperty,
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
rc = VcsBase::VcsBaseEditorWidget::getVcsBaseEditor(outputEditor);
DiffEditor::DiffEditorEditable *GitClient::findExistingDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue) const
{
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
if (!outputEditor)
return 0;
// Exists already
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
outputEditor->createNew(m_msgWait);
return qobject_cast<DiffEditor::DiffEditorEditable *>(outputEditor);
/* 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
* already open). */
VcsBase::VcsBaseEditorWidget *GitClient::createVcsEditor(const Core::Id &id,
QString title,
// Source file or directory
const QString &source,
CodecType codecType,
// Dynamic property and value to identify that editor
const char *registerDynamicProperty,
const QString &dynamicPropertyValue,
QWidget *configWidget) const
QTC_CHECK(!findExistingVCSEditor(registerDynamicProperty, dynamicPropertyValue));
// Create new, set wait message, set up with source and codec
Core::IEditor *outputEditor = Core::EditorManager::openEditorWithContents(id, &title, m_msgWait);
outputEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
rc = VcsBase::VcsBaseEditorWidget::getVcsBaseEditor(outputEditor);
connect(rc, SIGNAL(annotateRevisionRequested(QString,QString,int)),
this, SLOT(slotBlameRevisionRequested(QString,QString,int)));
QTC_ASSERT(rc, return 0);
rc->setSource(source);
if (codecType == CodecSource) {
rc->setCodec(getSourceCodec(source));
} else if (codecType == CodecLogOutput) {
QString encodingName = readConfigValue(source, QLatin1String("i18n.logOutputEncoding"));
if (encodingName.isEmpty())
encodingName = QLatin1String("utf-8");
rc->setCodec(QTextCodec::codecForName(encodingName.toLocal8Bit()));
}
rc->setForceReadOnly(true);
Core::EditorManager::activateEditor(outputEditor, Core::EditorManager::ModeSwitch);
if (configWidget)
rc->setConfigurationWidget(configWidget);

Friedemann Kleint
committed
void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs,
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames)
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
const Core::Id editorId = DiffEditor::Constants::DIFF_EDITOR_ID;
QString title = tr("Git Diff");
DiffEditor::DiffEditorEditable *editorEditable = findExistingDiffEditor("originalFileName", workingDirectory);
if (!editorEditable) {
editorEditable = qobject_cast<DiffEditor::DiffEditorEditable *>(
Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait));
editorEditable->document()->setProperty("originalFileName", workingDirectory);
Core::EditorManager::activateEditor(editorEditable, Core::EditorManager::ModeSwitch); // should probably go outside this block
int timeout = settings()->intValue(GitSettings::timeoutKey);
GitDiffHandler *handler = new GitDiffHandler(editorEditable, gitBinaryPath(), workingDirectory, processEnvironment(), 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);
// current project diff
handler->diffProjects(unstagedFileNames);
} else {
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()));
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
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);
}

Friedemann Kleint
committed
void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs,
const QString &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);
DiffEditor::DiffEditorEditable *editorEditable = findExistingDiffEditor("originalFileName", sourceFile);
if (!editorEditable) {
editorEditable = qobject_cast<DiffEditor::DiffEditorEditable *>(
Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait));
editorEditable->document()->setProperty("originalFileName", sourceFile);
Core::EditorManager::activateEditor(editorEditable, Core::EditorManager::ModeSwitch);
}
if (!fileName.isEmpty()) {
int timeout = settings()->intValue(GitSettings::timeoutKey);
GitDiffHandler *handler = new GitDiffHandler(editorEditable, gitBinaryPath(), workingDirectory, processEnvironment(), 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);
VcsBase::VcsBaseEditorWidget *editor = findExistingVCSEditor("originalFileName", sourceFile);
if (!editor) {
GitFileDiffArgumentsWidget *argWidget =