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/diffeditor.h>
#include <diffeditor/diffshoweditor.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";
static const char noColorOption[] = "--no-color";
static const char decorateOption[] = "--decorate";
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::DiffEditor *editor,
const QString &workingDirectory,
const QProcessEnvironment &environment,
int timeout);
// index -> working tree
void diffFile(const QString &fileName);
// stagedFileNames: HEAD -> index
// unstagedFileNames: index -> 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);
void slotShowDescriptionReceived(const QByteArray &data);
void slotFileListReceived(const QByteArray &data);
void slotFileContentsReceived(const QByteArray &data);
private:
void collectShowDescription(const QString &id);
void collectFilesList(const QStringList &additionalArguments);
void prepareForCollection();
void collectFilesContents();
void feedEditor();
QString workingTreeContents(const QString &fileName) const;
QPointer<DiffEditor::DiffEditor> 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::DiffEditor *editor,
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(WorkingTree));
collectFilesList(QStringList() << QLatin1String("--") << fileName);
}
void GitDiffHandler::diffFiles(const QStringList &stagedFileNames, const QStringList &unstagedFileNames)
{
RevisionRange stagedRange = RevisionRange(
Revision(Other, QLatin1String(HEAD)),
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(WorkingTree));
collectFilesList(QStringList() << QLatin1String("--") << projectPaths);
}
void GitDiffHandler::diffRepository()
{
m_requestedRevisionRange = RevisionRange(
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);
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
collectShowDescription(id);
}
void GitDiffHandler::collectShowDescription(const QString &id)
{
m_editor->clear(m_waitMessage);
VcsBase::Command *command = new VcsBase::Command(m_gitPath, m_workingDirectory, m_processEnvironment);
connect(command, SIGNAL(outputData(QByteArray)), this, SLOT(slotShowDescriptionReceived(QByteArray)));
QStringList arguments;
arguments << QLatin1String("show") << QLatin1String("-s") << QLatin1String("--format=fuller") << id;
command->addJob(arguments, m_timeout);
command->execute();
}
void GitDiffHandler::slotShowDescriptionReceived(const QByteArray &data)
{
const QString description = m_editor->editorWidget()->codec()->toUnicode(data).remove(QLatin1Char('\r'));
DiffEditor::DiffShowEditor *editor = qobject_cast<DiffEditor::DiffShowEditor *>(m_editor);
if (editor) {
editor->setDescription(description);
}
collectFilesList(QStringList()
<< m_requestedRevisionRange.begin.id
<< m_requestedRevisionRange.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).remove(QLatin1Char('\r'));
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;
}
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
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).remove(QLatin1Char('\r'));
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 &command = QString())
m_workingDirectory(workingDirectory),
m_command(command)
{
parentCommand->setExpectChanges(true);
connect(parentCommand, SIGNAL(outputData(QByteArray)), this, SLOT(readStdOut(QByteArray)));
connect(parentCommand, SIGNAL(errorText(QString)), this, SLOT(readStdErr(QString)));
}
GitClient *client = GitPlugin::instance()->gitClient();
if (m_commit.isEmpty() && m_files.isEmpty()) {
if (client->checkCommandInProgress(m_workingDirectory) == GitClient::NoCommand)
client->endStashScope(m_workingDirectory);
client->handleMergeConflicts(m_workingDirectory, m_commit, m_files, m_command);
}
void readStdOutString(const QString &data)
{
static QRegExp patchFailedRE(QLatin1String("Patch failed at ([^\\n]*)"));
static QRegExp conflictedFilesRE(QLatin1String("Merge conflict in ([^\\n]*)"));
if (patchFailedRE.indexIn(data) != -1)
m_commit = patchFailedRE.cap(1);
int fileIndex = -1;
while ((fileIndex = conflictedFilesRE.indexIn(data, fileIndex + 1)) != -1) {
m_files.append(conflictedFilesRE.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_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());
}
GitClient::~GitClient()
{
}
QString GitClient::findRepositoryForDirectory(const QString &dir)
{
if (dir.isEmpty() || dir.endsWith(QLatin1String("/.git"))
|| dir.contains(QLatin1String("/.git/"))) {
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;
VcsBase::VcsBaseEditorWidget *GitClient::findExistingVCSEditor(const char *registerDynamicProperty,
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
Core::EditorManager::activateEditor(outputEditor);
rc = VcsBase::VcsBaseEditorWidget::getVcsBaseEditor(outputEditor);
DiffEditor::DiffEditor *GitClient::findExistingOrOpenNewDiffEditor(const char *registerDynamicProperty,
const QString &dynamicPropertyValue, const QString &titlePattern, const Core::Id editorId) const
{
Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
if (outputEditor) {
// Exists already
Core::EditorManager::activateEditor(outputEditor);
outputEditor->createNew(m_msgWait);
}
DiffEditor::DiffEditor *editor = qobject_cast<DiffEditor::DiffEditor *>(outputEditor);
if (!editor) {
QString title = titlePattern;
editor = qobject_cast<DiffEditor::DiffEditor *>(
Core::EditorManager::openEditorWithContents(editorId, &title, m_msgWait));
editor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
Core::EditorManager::activateEditor(editor);
/* 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);
if (configWidget)
rc->setConfigurationWidget(configWidget);

Friedemann Kleint
committed
void GitClient::diff(const QString &workingDirectory,
const QStringList &diffArgs,
const QStringList &unstagedFileNames,
const QStringList &stagedFileNames)
const QString title = tr("Git Diff");
const int timeout = settings()->intValue(GitSettings::timeoutKey);
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {
DiffEditor::DiffEditor *editor = findExistingOrOpenNewDiffEditor(
"originalFileName",
workingDirectory,
title,
DiffEditor::Constants::DIFF_EDITOR_ID);
GitDiffHandler *handler = new GitDiffHandler(editor, 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;
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()));
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
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);
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)
const QString title = tr("Git Diff \"%1\"").arg(fileName);
if (settings()->boolValue(GitSettings::useDiffEditorKey)) {