-
-
Notifications
You must be signed in to change notification settings - Fork 113
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multi file tool support #535
base: master
Are you sure you want to change the base?
Changes from 17 commits
4e5bfd8
76f07e7
959c83d
01b09f4
53cb4ff
db90aa8
f51f2d0
ab69697
b17e494
9a67cee
e20b5d0
e8c9d13
99537fa
1b37de0
fb6218c
c5be6b3
3e34413
11d27ed
cc117e6
87a0722
26f2513
009e67f
11f2304
c544198
f9848ca
76031bb
59a51da
304d037
02f2f39
9424dde
64be458
9da5e4f
6b77e6e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,11 @@ | ||
build | ||
.cache | ||
.DS_Store | ||
.project | ||
.vscode/ | ||
CMakeLists.txt.user | ||
cmake-build-debug/ | ||
cmake-build-release/ | ||
.idea/ | ||
compile_commands.json | ||
|
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -10,17 +10,23 @@ | |||||||||
#include "DiffTool.h" | ||||||||||
#include "git/Command.h" | ||||||||||
#include "git/Repository.h" | ||||||||||
#include "git/Blob.h" | ||||||||||
#include <QProcess> | ||||||||||
#include <QTemporaryFile> | ||||||||||
#include <QDebug> | ||||||||||
|
||||||||||
DiffTool::DiffTool(const QString &file, const git::Blob &localBlob, | ||||||||||
const git::Blob &remoteBlob, QObject *parent) | ||||||||||
: ExternalTool(file, parent), mLocalBlob(localBlob), | ||||||||||
mRemoteBlob(remoteBlob) {} | ||||||||||
DiffTool::DiffTool(const QStringList &files, const git::Diff &diff, | ||||||||||
const git::Repository &repo, QObject *parent) | ||||||||||
: ExternalTool(files, diff, repo, parent) {} | ||||||||||
|
||||||||||
bool DiffTool::isValid() const { | ||||||||||
return (ExternalTool::isValid() && mLocalBlob.isValid()); | ||||||||||
bool isValid = ExternalTool::isValid(); | ||||||||||
foreach (const QString &file, mFiles) { | ||||||||||
git::Blob fileBlob; | ||||||||||
isValid &= DiffTool::getBlob(file, git::Diff::OldFile, fileBlob) & | ||||||||||
fileBlob.isValid(); | ||||||||||
}; | ||||||||||
return isValid; | ||||||||||
} | ||||||||||
|
||||||||||
ExternalTool::Kind DiffTool::kind() const { return Diff; } | ||||||||||
|
@@ -35,76 +41,105 @@ bool DiffTool::start() { | |||||||||
if (command.isEmpty()) | ||||||||||
return false; | ||||||||||
|
||||||||||
// Write temporary files. | ||||||||||
QString templatePath = QDir::temp().filePath(QFileInfo(mFile).fileName()); | ||||||||||
QTemporaryFile *local = new QTemporaryFile(templatePath, this); | ||||||||||
if (!local->open()) | ||||||||||
return false; | ||||||||||
|
||||||||||
local->write(mLocalBlob.content()); | ||||||||||
local->flush(); | ||||||||||
|
||||||||||
QString remotePath; | ||||||||||
if (!mRemoteBlob.isValid()) { | ||||||||||
remotePath = mFile; | ||||||||||
} else { | ||||||||||
QTemporaryFile *remote = new QTemporaryFile(templatePath, this); | ||||||||||
if (!remote->open()) | ||||||||||
return false; | ||||||||||
|
||||||||||
remote->write(mRemoteBlob.content()); | ||||||||||
remote->flush(); | ||||||||||
|
||||||||||
remotePath = remote->fileName(); | ||||||||||
} | ||||||||||
|
||||||||||
// Destroy this after process finishes. | ||||||||||
QProcess *process = new QProcess(this); | ||||||||||
process->setProcessChannelMode( | ||||||||||
QProcess::ProcessChannelMode::ForwardedChannels); | ||||||||||
auto signal = QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished); | ||||||||||
QObject::connect(process, signal, [this, process] { | ||||||||||
qDebug() << "Merge Process Exited!"; | ||||||||||
qDebug() << "Stdout: " << process->readAllStandardOutput(); | ||||||||||
qDebug() << "Stderr: " << process->readAllStandardError(); | ||||||||||
deleteLater(); | ||||||||||
}); | ||||||||||
bool isWorkDirDiff = mDiff.isValid() && mDiff.isStatusDiff(); | ||||||||||
|
||||||||||
int numFiles = mFiles.size(); | ||||||||||
foreach (const QString &filePathAndName, mFiles) { | ||||||||||
git::Blob filePathOld, filePathNew; | ||||||||||
if (!getBlob(filePathAndName, git::Diff::OldFile, filePathOld) || | ||||||||||
!getBlob(filePathAndName, git::Diff::NewFile, filePathNew)) | ||||||||||
continue; | ||||||||||
|
||||||||||
// Get the path to the file (either a full or relative path). | ||||||||||
QString otherPathAndName = filePathAndName; | ||||||||||
if (filePathOld.isValid()) { | ||||||||||
otherPathAndName = makeBlobTempFullFilePath(filePathAndName, filePathOld); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
if (otherPathAndName.isEmpty()) | ||||||||||
return false; | ||||||||||
} | ||||||||||
|
||||||||||
// Destroy this after process finishes. | ||||||||||
QProcess *process = new QProcess(); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
process->setProcessChannelMode( | ||||||||||
QProcess::ProcessChannelMode::ForwardedChannels); | ||||||||||
auto signal = QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished); | ||||||||||
QObject::connect(process, signal, [this, process, &numFiles] { | ||||||||||
qDebug() << "Merge Process Exited!"; | ||||||||||
qDebug() << "Stdout: " << process->readAllStandardOutput(); | ||||||||||
qDebug() << "Stderr: " << process->readAllStandardError(); | ||||||||||
if (--numFiles == 0) { | ||||||||||
deleteLater(); | ||||||||||
} | ||||||||||
Comment on lines
+70
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
}); | ||||||||||
|
||||||||||
// Convert to absolute path. | ||||||||||
QString fullFilePath = | ||||||||||
isWorkDirDiff ? mRepo.workdir().filePath(filePathAndName) | ||||||||||
: makeBlobTempFullFilePath(filePathAndName, filePathNew); | ||||||||||
|
||||||||||
#if defined(FLATPAK) || defined(DEBUG_FLATPAK) | ||||||||||
QStringList arguments = {"--host", "--env=LOCAL=" + local->fileName(), | ||||||||||
"--env=REMOTE=" + remotePath, | ||||||||||
"--env=MERGED=" + mFile, "--env=BASE=" + mFile}; | ||||||||||
arguments.append("sh"); | ||||||||||
arguments.append("-c"); | ||||||||||
arguments.append(command); | ||||||||||
process->start("flatpak-spawn", arguments); | ||||||||||
QStringList arguments = {"--host", "--env=LOCAL=" + fullFilePath, | ||||||||||
"--env=REMOTE=" + otherPathAndName, | ||||||||||
"--env=MERGED=" + filePathAndName, | ||||||||||
"--env=BASE=" + filePathAndName}; | ||||||||||
arguments.append("sh"); | ||||||||||
arguments.append("-c"); | ||||||||||
arguments.append(command); | ||||||||||
process->start("flatpak-spawn", arguments); | ||||||||||
#else | ||||||||||
|
||||||||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); | ||||||||||
env.insert("LOCAL", local->fileName()); | ||||||||||
env.insert("REMOTE", remotePath); | ||||||||||
env.insert("MERGED", mFile); | ||||||||||
env.insert("BASE", mFile); | ||||||||||
process->setProcessEnvironment(env); | ||||||||||
|
||||||||||
QString bash = git::Command::bashPath(); | ||||||||||
if (!bash.isEmpty()) { | ||||||||||
process->start(bash, {"-c", command}); | ||||||||||
} else if (!shell) { | ||||||||||
process->start(git::Command::substitute(env, command)); | ||||||||||
} else { | ||||||||||
emit error(BashNotFound); | ||||||||||
return false; | ||||||||||
} | ||||||||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); | ||||||||||
env.insert("LOCAL", fullFilePath); | ||||||||||
env.insert("REMOTE", otherPathAndName); | ||||||||||
env.insert("MERGED", filePathAndName); | ||||||||||
env.insert("BASE", filePathAndName); | ||||||||||
process->setProcessEnvironment(env); | ||||||||||
|
||||||||||
QString bash = git::Command::bashPath(); | ||||||||||
if (!bash.isEmpty()) { | ||||||||||
process->start(bash, {"-c", command}); | ||||||||||
} else if (!shell) { | ||||||||||
process->start(git::Command::substitute(env, command)); | ||||||||||
} else { | ||||||||||
emit error(BashNotFound); | ||||||||||
return false; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
#endif | ||||||||||
|
||||||||||
if (!process->waitForStarted()) { | ||||||||||
qDebug() << "DiffTool starting failed"; | ||||||||||
return false; | ||||||||||
if (!process->waitForStarted()) { | ||||||||||
qDebug() << "DiffTool starting failed"; | ||||||||||
return false; | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
} | ||||||||||
|
||||||||||
// Detach from parent. | ||||||||||
setParent(nullptr); | ||||||||||
|
||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
bool DiffTool::getBlob(const QString &file, const git::Diff::File &version, | ||||||||||
git::Blob &blob) const { | ||||||||||
int index = mDiff.indexOf(file); | ||||||||||
if (index < 0) | ||||||||||
return false; | ||||||||||
|
||||||||||
blob = mRepo.lookupBlob(mDiff.id(index, version)); | ||||||||||
return true; | ||||||||||
} | ||||||||||
|
||||||||||
QString DiffTool::makeBlobTempFullFilePath(const QString &filePathAndName, | ||||||||||
const git::Blob &fileBlob) { | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
QString blobTmpFullFilePath; | ||||||||||
|
||||||||||
QFileInfo fileInfo(filePathAndName); | ||||||||||
QString templatePath = QDir::temp().filePath(fileInfo.fileName()); | ||||||||||
QTemporaryFile *temp = new QTemporaryFile(templatePath, this); | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
if (temp->open()) { | ||||||||||
temp->write(fileBlob.content()); | ||||||||||
temp->flush(); | ||||||||||
blobTmpFullFilePath = temp->fileName(); | ||||||||||
} | ||||||||||
|
||||||||||
return blobTmpFullFilePath; | ||||||||||
} |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -10,15 +10,23 @@ | |||||
#ifndef DIFFTOOL_H | ||||||
#define DIFFTOOL_H | ||||||
|
||||||
#include <QString> | ||||||
#include "ExternalTool.h" | ||||||
#include "git/Blob.h" | ||||||
|
||||||
class QObject; | ||||||
class QString; | ||||||
namespace git { | ||||||
class Diff; | ||||||
class Repository; | ||||||
class Blob; | ||||||
}; // namespace git | ||||||
|
||||||
class DiffTool : public ExternalTool { | ||||||
Q_OBJECT | ||||||
|
||||||
public: | ||||||
DiffTool(const QString &file, const git::Blob &localBlob, | ||||||
const git::Blob &remoteBlob, QObject *parent = nullptr); | ||||||
DiffTool(const QStringList &files, const git::Diff &diff, | ||||||
const git::Repository &repo, QObject *parent); | ||||||
|
||||||
bool isValid() const override; | ||||||
|
||||||
|
@@ -28,8 +36,12 @@ class DiffTool : public ExternalTool { | |||||
bool start() override; | ||||||
|
||||||
protected: | ||||||
git::Blob mLocalBlob; | ||||||
git::Blob mRemoteBlob; | ||||||
private: | ||||||
bool getBlob(const QString &file, const git::Diff::File &version, | ||||||
git::Blob &blob) const; | ||||||
|
||||||
QString makeBlobTempFullFilePath(const QString &filePathAndName, | ||||||
const git::Blob &fileBlob); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
}; | ||||||
|
||||||
#endif |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -14,11 +14,19 @@ | |
#include <QProcess> | ||
#include <QUrl> | ||
|
||
EditTool::EditTool(const QString &file, QObject *parent) | ||
: ExternalTool(file, parent) {} | ||
EditTool::EditTool(const QStringList &files, const git::Diff &diff, | ||
const git::Repository &repo, QObject *parent) | ||
: ExternalTool(files, diff, repo, parent) {} | ||
|
||
bool EditTool::isValid() const { | ||
return (ExternalTool::isValid() && QFileInfo(mFile).isFile()); | ||
if (!ExternalTool::isValid()) | ||
return false; | ||
|
||
foreach (const QString file, mFiles) { | ||
if (!QFileInfo(mRepo.workdir().filePath(file)).isFile()) | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
ExternalTool::Kind EditTool::kind() const { return Edit; } | ||
|
@@ -27,26 +35,31 @@ QString EditTool::name() const { return tr("Edit in External Editor"); } | |
|
||
bool EditTool::start() { | ||
git::Config config = git::Config::global(); | ||
QString editor = config.value<QString>("gui.editor"); | ||
QString baseEditor = config.value<QString>("gui.editor"); | ||
|
||
if (editor.isEmpty()) | ||
editor = qgetenv("GIT_EDITOR"); | ||
if (baseEditor.isEmpty()) | ||
baseEditor = qgetenv("GIT_EDITOR"); | ||
|
||
if (editor.isEmpty()) | ||
editor = config.value<QString>("core.editor"); | ||
if (baseEditor.isEmpty()) | ||
baseEditor = config.value<QString>("core.editor"); | ||
|
||
if (editor.isEmpty()) | ||
editor = qgetenv("VISUAL"); | ||
if (baseEditor.isEmpty()) | ||
baseEditor = qgetenv("VISUAL"); | ||
|
||
if (editor.isEmpty()) | ||
editor = qgetenv("EDITOR"); | ||
if (baseEditor.isEmpty()) | ||
baseEditor = qgetenv("EDITOR"); | ||
|
||
if (editor.isEmpty()) | ||
return QDesktopServices::openUrl(QUrl::fromLocalFile(mFile)); | ||
if (baseEditor.isEmpty()) { | ||
foreach (const QString &file, mFiles) { | ||
QDesktopServices::openUrl(QUrl::fromLocalFile(file)); | ||
} | ||
return true; | ||
} | ||
|
||
// Find arguments. | ||
QStringList args = editor.split("\" \""); | ||
QString editor = baseEditor; | ||
|
||
// Find arguments. | ||
QStringList args = baseEditor.split("\" \""); | ||
if (args.count() > 1) { | ||
// Format 1: "Command" "Argument1" "Argument2" | ||
editor = args[0]; | ||
|
@@ -62,17 +75,24 @@ bool EditTool::start() { | |
editor = editor.left(li + 1); | ||
} else { | ||
// Format 3: "Command" (no argument) | ||
// Format 4: Command (no argument) | ||
if (fi == -1) { | ||
// Format 4: Command Argument1 Argument2 | ||
// Format 5: Command (no argument) | ||
args = editor.split(" "); | ||
editor = args.size() ? args[0] : ""; | ||
} | ||
} | ||
} | ||
|
||
// Remove command, add filename, trim command. | ||
args.removeFirst(); | ||
args.append(mFile); | ||
foreach (const QString &file, mFiles) { | ||
args.append(mRepo.workdir().filePath(file)); | ||
} | ||
editor.remove("\""); | ||
|
||
// Destroy this after process finishes. | ||
QProcess *process = new QProcess(this); | ||
QProcess *process = new QProcess(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you have to be carefull here, because without parent, the process object has to be deleted manually. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, that accounts for the cryptic comment. 😉 I'll review this. |
||
auto signal = QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished); | ||
QObject::connect(process, signal, this, &ExternalTool::deleteLater); | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.