forked from qt-creator/qt-creator
Merge branch 'master' of scm.dev.nokia.troll.no:creator/mainline
This commit is contained in:
@@ -41,8 +41,11 @@ class CORE_EXPORT IVersionControl : public QObject
|
|||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
enum Operation { AddOperation, DeleteOperation, OpenOperation,
|
enum Operation {
|
||||||
CreateRepositoryOperation };
|
AddOperation, DeleteOperation, OpenOperation,
|
||||||
|
CreateRepositoryOperation,
|
||||||
|
SnapshotOperations
|
||||||
|
};
|
||||||
|
|
||||||
explicit IVersionControl(QObject *parent = 0) : QObject(parent) {}
|
explicit IVersionControl(QObject *parent = 0) : QObject(parent) {}
|
||||||
virtual ~IVersionControl() {}
|
virtual ~IVersionControl() {}
|
||||||
@@ -98,10 +101,31 @@ public:
|
|||||||
virtual bool vcsDelete(const QString &filename) = 0;
|
virtual bool vcsDelete(const QString &filename) = 0;
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
* Called to initialize the version control systemin a directory.
|
* Called to initialize the version control system in a directory.
|
||||||
*/
|
*/
|
||||||
virtual bool vcsCreateRepository(const QString &directory) = 0;
|
virtual bool vcsCreateRepository(const QString &directory) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Create a snapshot of the current state and return an identifier or
|
||||||
|
* an empty string in case of failure.
|
||||||
|
*/
|
||||||
|
virtual QString vcsCreateSnapshot(const QString &topLevel) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* List snapshots.
|
||||||
|
*/
|
||||||
|
virtual QStringList vcsSnapshots(const QString &topLevel) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Restore a snapshot.
|
||||||
|
*/
|
||||||
|
virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name) = 0;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
* Remove a snapshot.
|
||||||
|
*/
|
||||||
|
virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name) = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void repositoryChanged(const QString &repository);
|
void repositoryChanged(const QString &repository);
|
||||||
void filesChanged(const QStringList &files);
|
void filesChanged(const QStringList &files);
|
||||||
|
@@ -55,6 +55,7 @@ bool CVSControl::supportsOperation(Operation operation) const
|
|||||||
break;
|
break;
|
||||||
case OpenOperation:
|
case OpenOperation:
|
||||||
case CreateRepositoryOperation:
|
case CreateRepositoryOperation:
|
||||||
|
case SnapshotOperations:
|
||||||
rc = false;
|
rc = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -84,6 +85,26 @@ bool CVSControl::vcsCreateRepository(const QString &)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString CVSControl::vcsCreateSnapshot(const QString &)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList CVSControl::vcsSnapshots(const QString &)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CVSControl::vcsRestoreSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CVSControl::vcsRemoveSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool CVSControl::managesDirectory(const QString &directory) const
|
bool CVSControl::managesDirectory(const QString &directory) const
|
||||||
{
|
{
|
||||||
return m_plugin->managesDirectory(directory);
|
return m_plugin->managesDirectory(directory);
|
||||||
|
@@ -53,6 +53,10 @@ public:
|
|||||||
virtual bool vcsAdd(const QString &fileName);
|
virtual bool vcsAdd(const QString &fileName);
|
||||||
virtual bool vcsDelete(const QString &filename);
|
virtual bool vcsDelete(const QString &filename);
|
||||||
virtual bool vcsCreateRepository(const QString &directory);
|
virtual bool vcsCreateRepository(const QString &directory);
|
||||||
|
virtual QString vcsCreateSnapshot(const QString &topLevel);
|
||||||
|
virtual QStringList vcsSnapshots(const QString &topLevel);
|
||||||
|
virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
|
||||||
void emitRepositoryChanged(const QString &s);
|
void emitRepositoryChanged(const QString &s);
|
||||||
void emitFilesChanged(const QStringList &l);
|
void emitFilesChanged(const QStringList &l);
|
||||||
|
@@ -22,7 +22,9 @@ HEADERS += gitplugin.h \
|
|||||||
branchmodel.h \
|
branchmodel.h \
|
||||||
gitcommand.h \
|
gitcommand.h \
|
||||||
clonewizard.h \
|
clonewizard.h \
|
||||||
clonewizardpage.h
|
clonewizardpage.h \
|
||||||
|
stashdialog.h \
|
||||||
|
gitutils.h
|
||||||
SOURCES += gitplugin.cpp \
|
SOURCES += gitplugin.cpp \
|
||||||
gitclient.cpp \
|
gitclient.cpp \
|
||||||
changeselectiondialog.cpp \
|
changeselectiondialog.cpp \
|
||||||
@@ -38,11 +40,13 @@ SOURCES += gitplugin.cpp \
|
|||||||
branchmodel.cpp \
|
branchmodel.cpp \
|
||||||
gitcommand.cpp \
|
gitcommand.cpp \
|
||||||
clonewizard.cpp \
|
clonewizard.cpp \
|
||||||
clonewizardpage.cpp
|
clonewizardpage.cpp \
|
||||||
|
stashdialog.cpp \
|
||||||
|
gitutils.cpp
|
||||||
FORMS += changeselectiondialog.ui \
|
FORMS += changeselectiondialog.ui \
|
||||||
settingspage.ui \
|
settingspage.ui \
|
||||||
gitsubmitpanel.ui \
|
gitsubmitpanel.ui \
|
||||||
branchdialog.ui
|
branchdialog.ui \
|
||||||
|
stashdialog.ui
|
||||||
OTHER_FILES += ScmGit.pluginspec
|
OTHER_FILES += ScmGit.pluginspec
|
||||||
include(gitorious/gitorious.pri)
|
include(gitorious/gitorious.pri)
|
||||||
|
@@ -29,6 +29,7 @@
|
|||||||
|
|
||||||
#include "gitclient.h"
|
#include "gitclient.h"
|
||||||
#include "gitcommand.h"
|
#include "gitcommand.h"
|
||||||
|
#include "gitutils.h"
|
||||||
|
|
||||||
#include "commitdata.h"
|
#include "commitdata.h"
|
||||||
#include "gitconstants.h"
|
#include "gitconstants.h"
|
||||||
@@ -64,15 +65,12 @@
|
|||||||
#include <QtGui/QMessageBox>
|
#include <QtGui/QMessageBox>
|
||||||
#include <QtGui/QPushButton>
|
#include <QtGui/QPushButton>
|
||||||
|
|
||||||
using namespace Git;
|
|
||||||
using namespace Git::Internal;
|
|
||||||
|
|
||||||
static const char *const kGitDirectoryC = ".git";
|
static const char *const kGitDirectoryC = ".git";
|
||||||
static const char *const kBranchIndicatorC = "# On branch";
|
static const char *const kBranchIndicatorC = "# On branch";
|
||||||
|
|
||||||
static inline QString msgServerFailure()
|
static inline QString msgServerFailure()
|
||||||
{
|
{
|
||||||
return GitClient::tr(
|
return Git::Internal::GitClient::tr(
|
||||||
"Note that the git plugin for QtCreator is not able to interact with the server "
|
"Note that the git plugin for QtCreator is not able to interact with the server "
|
||||||
"so far. Thus, manual ssh-identification etc. will not work.");
|
"so far. Thus, manual ssh-identification etc. will not work.");
|
||||||
}
|
}
|
||||||
@@ -85,6 +83,29 @@ inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace Git {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
static inline QString msgRepositoryNotFound(const QString &dir)
|
static inline QString msgRepositoryNotFound(const QString &dir)
|
||||||
{
|
{
|
||||||
return GitClient::tr("Unable to determine the repository for %1.").arg(dir);
|
return GitClient::tr("Unable to determine the repository for %1.").arg(dir);
|
||||||
@@ -103,6 +124,9 @@ static QString formatCommand(const QString &binary, const QStringList &args)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ---------------- GitClient
|
// ---------------- GitClient
|
||||||
|
|
||||||
|
const char *GitClient::stashNamePrefix = "stash@{";
|
||||||
|
|
||||||
GitClient::GitClient(GitPlugin* plugin)
|
GitClient::GitClient(GitPlugin* plugin)
|
||||||
: m_msgWait(tr("Waiting for data...")),
|
: m_msgWait(tr("Waiting for data...")),
|
||||||
m_plugin(plugin),
|
m_plugin(plugin),
|
||||||
@@ -364,6 +388,30 @@ void GitClient::checkoutBranch(const QString &workingDirectory, const QString &b
|
|||||||
connectRepositoryChanged(workingDirectory, cmd);
|
connectRepositoryChanged(workingDirectory, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitClient::synchronousCheckoutBranch(const QString &workingDirectory,
|
||||||
|
const QString &branch,
|
||||||
|
QString *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
QByteArray outputText;
|
||||||
|
QByteArray errorText;
|
||||||
|
QStringList arguments;
|
||||||
|
arguments << QLatin1String("checkout") << branch;
|
||||||
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
|
const QString output = commandOutputFromLocal8Bit(outputText);
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(output);
|
||||||
|
if (!rc) {
|
||||||
|
const QString stdErr = commandOutputFromLocal8Bit(errorText);
|
||||||
|
const QString msg = tr("Unable to checkout %1 of %2: %3").arg(branch, workingDirectory, stdErr);
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GitClient::checkout(const QString &workingDirectory, const QString &fileName)
|
void GitClient::checkout(const QString &workingDirectory, const QString &fileName)
|
||||||
{
|
{
|
||||||
// Passing an empty argument as the file name is very dangereous, since this makes
|
// Passing an empty argument as the file name is very dangereous, since this makes
|
||||||
@@ -408,22 +456,12 @@ bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringLis
|
|||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()).
|
const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()).
|
||||||
arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitClient::synchronousReset(const QString &workingDirectory,
|
|
||||||
const QStringList &files)
|
|
||||||
{
|
|
||||||
QString errorMessage;
|
|
||||||
const bool rc = synchronousReset(workingDirectory, files, &errorMessage);
|
|
||||||
if (!rc)
|
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool GitClient::synchronousReset(const QString &workingDirectory,
|
bool GitClient::synchronousReset(const QString &workingDirectory,
|
||||||
const QStringList &files,
|
const QStringList &files,
|
||||||
QString *errorMessage)
|
QString *errorMessage)
|
||||||
@@ -433,14 +471,27 @@ bool GitClient::synchronousReset(const QString &workingDirectory,
|
|||||||
QByteArray outputText;
|
QByteArray outputText;
|
||||||
QByteArray errorText;
|
QByteArray errorText;
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
arguments << QLatin1String("reset") << QLatin1String("HEAD") << QLatin1String("--") << files;
|
arguments << QLatin1String("reset");
|
||||||
|
if (files.isEmpty()) {
|
||||||
|
arguments << QLatin1String("--hard");
|
||||||
|
} else {
|
||||||
|
arguments << QLatin1String("HEAD") << QLatin1String("--") << files;
|
||||||
|
}
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
const QString output = QString::fromLocal8Bit(outputText);
|
const QString output = commandOutputFromLocal8Bit(outputText);
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(output);
|
VCSBase::VCSBaseOutputWindow::instance()->append(output);
|
||||||
// Note that git exits with 1 even if the operation is successful
|
// Note that git exits with 1 even if the operation is successful
|
||||||
// Assume real failure if the output does not contain "foo.cpp modified"
|
// Assume real failure if the output does not contain "foo.cpp modified"
|
||||||
if (!rc && !output.contains(QLatin1String("modified"))) {
|
if (!rc && !output.contains(QLatin1String("modified"))) {
|
||||||
*errorMessage = tr("Unable to reset %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
const QString stdErr = commandOutputFromLocal8Bit(errorText);
|
||||||
|
const QString msg = files.isEmpty() ?
|
||||||
|
tr("Unable to reset %1: %2").arg(workingDirectory, stdErr) :
|
||||||
|
tr("Unable to reset %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, stdErr);
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -456,25 +507,41 @@ bool GitClient::synchronousInit(const QString &workingDirectory)
|
|||||||
const QStringList arguments(QLatin1String("init"));
|
const QStringList arguments(QLatin1String("init"));
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
// '[Re]Initialized...'
|
// '[Re]Initialized...'
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(QString::fromLocal8Bit(outputText));
|
VCSBase::VCSBaseOutputWindow::instance()->append(commandOutputFromLocal8Bit(outputText));
|
||||||
if (!rc)
|
if (!rc)
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(QString::fromLocal8Bit(errorText));
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(commandOutputFromLocal8Bit(errorText));
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitClient::synchronousCheckout(const QString &workingDirectory,
|
/* Checkout, supports:
|
||||||
const QStringList &files,
|
* git checkout -- <files>
|
||||||
QString *errorMessage)
|
* git checkout revision -- <files>
|
||||||
|
* git checkout revision -- . */
|
||||||
|
bool GitClient::synchronousCheckoutFiles(const QString &workingDirectory,
|
||||||
|
QStringList files /* = QStringList() */,
|
||||||
|
QString revision /* = QString() */,
|
||||||
|
QString *errorMessage /* = 0 */)
|
||||||
{
|
{
|
||||||
if (Git::Constants::debug)
|
if (Git::Constants::debug)
|
||||||
qDebug() << Q_FUNC_INFO << workingDirectory << files;
|
qDebug() << Q_FUNC_INFO << workingDirectory << files;
|
||||||
|
if (revision.isEmpty())
|
||||||
|
revision = QLatin1String("HEAD");
|
||||||
|
if (files.isEmpty())
|
||||||
|
files = QStringList(QString(QLatin1Char('.')));
|
||||||
QByteArray outputText;
|
QByteArray outputText;
|
||||||
QByteArray errorText;
|
QByteArray errorText;
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
arguments << QLatin1String("checkout") << QLatin1String("--") << files;
|
arguments << QLatin1String("checkout") << revision << QLatin1String("--") << files;
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = tr("Unable to checkout %n file(s) in %1: %2", 0, files.size()).arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
const QString fileArg = files.join(QLatin1String(", "));
|
||||||
|
const QString msg = tr("Unable to checkout %1 of %2 in %3: %4").
|
||||||
|
arg(revision, fileArg, workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(msg);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -533,13 +600,12 @@ bool GitClient::synchronousParentRevisions(const QString &workingDirectory,
|
|||||||
}
|
}
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, QString::fromLocal8Bit(errorText));
|
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, commandOutputFromLocal8Bit(errorText));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// Should result in one line of blank-delimited revisions, specifying current first
|
// Should result in one line of blank-delimited revisions, specifying current first
|
||||||
// unless it is top.
|
// unless it is top.
|
||||||
QString outputText = QString::fromLocal8Bit(outputTextData);
|
QString outputText = commandOutputFromLocal8Bit(outputTextData);
|
||||||
outputText.remove(QLatin1Char('\r'));
|
|
||||||
outputText.remove(QLatin1Char('\n'));
|
outputText.remove(QLatin1Char('\n'));
|
||||||
if (!splitCommitParents(outputText, 0, parents)) {
|
if (!splitCommitParents(outputText, 0, parents)) {
|
||||||
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, msgInvalidRevision());
|
*errorMessage = msgParentRevisionFailed(workingDirectory, revision, msgInvalidRevision());
|
||||||
@@ -578,6 +644,75 @@ bool GitClient::synchronousShortDescriptions(const QString &workingDirectory, co
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline QString msgCannotDetermineBranch(const QString &workingDirectory, const QString &why)
|
||||||
|
{
|
||||||
|
return GitClient::tr("Unable to retrieve branch of %1: %2").arg(workingDirectory, why);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve head revision/branch
|
||||||
|
bool GitClient::synchronousTopRevision(const QString &workingDirectory,
|
||||||
|
QString *revision /* = 0 */,
|
||||||
|
QString *branch /* = 0 */,
|
||||||
|
QString *errorMessageIn /* = 0 */)
|
||||||
|
{
|
||||||
|
if (Git::Constants::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << workingDirectory;
|
||||||
|
QByteArray outputTextData;
|
||||||
|
QByteArray errorText;
|
||||||
|
QStringList arguments;
|
||||||
|
QString errorMessage;
|
||||||
|
do {
|
||||||
|
// get revision
|
||||||
|
if (revision) {
|
||||||
|
revision->clear();
|
||||||
|
arguments << QLatin1String("log") << QLatin1String(noColorOption)
|
||||||
|
<< QLatin1String("--max-count=1") << QLatin1String("--pretty=format:%H");
|
||||||
|
if (!synchronousGit(workingDirectory, arguments, &outputTextData, &errorText)) {
|
||||||
|
errorMessage = tr("Unable to retrieve top revision of %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
*revision = commandOutputFromLocal8Bit(outputTextData);
|
||||||
|
revision->remove(QLatin1Char('\n'));
|
||||||
|
} // revision desired
|
||||||
|
// get branch
|
||||||
|
if (branch) {
|
||||||
|
branch->clear();
|
||||||
|
arguments.clear();
|
||||||
|
arguments << QLatin1String("branch") << QLatin1String(noColorOption);
|
||||||
|
if (!synchronousGit(workingDirectory, arguments, &outputTextData, &errorText)) {
|
||||||
|
errorMessage = msgCannotDetermineBranch(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
/* parse output for current branch: \code
|
||||||
|
* master
|
||||||
|
branch2
|
||||||
|
\endcode */
|
||||||
|
const QString branchPrefix = QLatin1String("* ");
|
||||||
|
foreach(const QString &line, commandOutputLinesFromLocal8Bit(outputTextData)) {
|
||||||
|
if (line.startsWith(branchPrefix)) {
|
||||||
|
*branch = line;
|
||||||
|
branch->remove(0, branchPrefix.size());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (branch->isEmpty()) {
|
||||||
|
errorMessage = msgCannotDetermineBranch(workingDirectory,
|
||||||
|
QString::fromLatin1("Internal error: Failed to parse output: %1").arg(commandOutputFromLocal8Bit(outputTextData)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} // branch
|
||||||
|
} while (false);
|
||||||
|
const bool failed = (revision && revision->isEmpty()) || (branch && branch->isEmpty());
|
||||||
|
if (failed && !errorMessage.isEmpty()) {
|
||||||
|
if (errorMessageIn) {
|
||||||
|
*errorMessageIn = errorMessage;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return !failed;
|
||||||
|
}
|
||||||
|
|
||||||
// Format an entry in a one-liner for selection list using git log.
|
// Format an entry in a one-liner for selection list using git log.
|
||||||
bool GitClient::synchronousShortDescription(const QString &workingDirectory,
|
bool GitClient::synchronousShortDescription(const QString &workingDirectory,
|
||||||
const QString &revision,
|
const QString &revision,
|
||||||
@@ -595,17 +730,83 @@ bool GitClient::synchronousShortDescription(const QString &workingDirectory,
|
|||||||
<< QLatin1String("--max-count=1") << revision;
|
<< QLatin1String("--max-count=1") << revision;
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputTextData, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = tr("Unable to describe revision %1 in %2: %3").arg(revision, workingDirectory, QString::fromLocal8Bit(errorText));
|
*errorMessage = tr("Unable to describe revision %1 in %2: %3").arg(revision, workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*description = QString::fromLocal8Bit(outputTextData);
|
*description = commandOutputFromLocal8Bit(outputTextData);
|
||||||
description->remove(QLatin1Char('\r'));
|
|
||||||
if (description->endsWith(QLatin1Char('\n')))
|
if (description->endsWith(QLatin1Char('\n')))
|
||||||
description->truncate(description->size() - 1);
|
description->truncate(description->size() - 1);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitClient::synchronousStash(const QString &workingDirectory, QString *errorMessage)
|
// Create a default message to be used for describing stashes
|
||||||
|
static inline QString creatorStashMessage(const QString &keyword = QString())
|
||||||
|
{
|
||||||
|
QString rc = QCoreApplication::applicationName();
|
||||||
|
rc += QLatin1Char(' ');
|
||||||
|
if (!keyword.isEmpty()) {
|
||||||
|
rc += keyword;
|
||||||
|
rc += QLatin1Char(' ');
|
||||||
|
}
|
||||||
|
rc += QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do a stash and return the message as identifier. Note that stash names (stash{n})
|
||||||
|
* shift as they are pushed, so, enforce the use of messages to identify them. Flags:
|
||||||
|
* StashPromptDescription: Prompt the user for a description message.
|
||||||
|
* StashImmediateRestore: Immediately re-apply this stash (used for snapshots), user keeps on working
|
||||||
|
* StashIgnoreUnchanged: Be quiet about unchanged repositories (used for IVersionControl's snapshots). */
|
||||||
|
|
||||||
|
QString GitClient::synchronousStash(const QString &workingDirectory,
|
||||||
|
const QString &messageKeyword /* = QString() */,
|
||||||
|
unsigned flags,
|
||||||
|
bool *unchanged /* =0 */)
|
||||||
|
{
|
||||||
|
if (unchanged)
|
||||||
|
*unchanged = false;
|
||||||
|
QString message;
|
||||||
|
bool success = false;
|
||||||
|
// Check for changes and stash
|
||||||
|
QString errorMessage;
|
||||||
|
switch (gitStatus(workingDirectory, false, 0, &errorMessage)) {
|
||||||
|
case StatusChanged: {
|
||||||
|
message = creatorStashMessage(messageKeyword);
|
||||||
|
do {
|
||||||
|
if ((flags & StashPromptDescription)) {
|
||||||
|
if (!inputText(Core::ICore::instance()->mainWindow(),
|
||||||
|
tr("Stash description"), tr("Description:"), &message))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!executeSynchronousStash(workingDirectory, message))
|
||||||
|
break;
|
||||||
|
if ((flags & StashImmediateRestore)
|
||||||
|
&& !synchronousStashRestore(workingDirectory, QLatin1String("stash@{0}")))
|
||||||
|
break;
|
||||||
|
success = true;
|
||||||
|
} while (false);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case StatusUnchanged:
|
||||||
|
if (unchanged)
|
||||||
|
*unchanged = true;
|
||||||
|
if (!(flags & StashIgnoreUnchanged))
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msgNoChangedFiles());
|
||||||
|
break;
|
||||||
|
case StatusFailed:
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!success)
|
||||||
|
message.clear();
|
||||||
|
if (Git::Constants::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << '\n' << workingDirectory << messageKeyword << "returns" << message;
|
||||||
|
return message;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GitClient::executeSynchronousStash(const QString &workingDirectory,
|
||||||
|
const QString &message,
|
||||||
|
QString *errorMessage /* = 0*/)
|
||||||
{
|
{
|
||||||
if (Git::Constants::debug)
|
if (Git::Constants::debug)
|
||||||
qDebug() << Q_FUNC_INFO << workingDirectory;
|
qDebug() << Q_FUNC_INFO << workingDirectory;
|
||||||
@@ -613,14 +814,50 @@ bool GitClient::synchronousStash(const QString &workingDirectory, QString *error
|
|||||||
QByteArray errorText;
|
QByteArray errorText;
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
arguments << QLatin1String("stash");
|
arguments << QLatin1String("stash");
|
||||||
|
if (!message.isEmpty())
|
||||||
|
arguments << QLatin1String("save") << message;
|
||||||
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = tr("Unable stash in %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
const QString msg = tr("Unable stash in %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Resolve a stash name from message
|
||||||
|
bool GitClient::stashNameFromMessage(const QString &workingDirectory,
|
||||||
|
const QString &message, QString *name,
|
||||||
|
QString *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
// All happy
|
||||||
|
if (message.startsWith(QLatin1String(stashNamePrefix))) {
|
||||||
|
*name = message;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Retrieve list and find via message
|
||||||
|
QList<Stash> stashes;
|
||||||
|
if (!synchronousStashList(workingDirectory, &stashes, errorMessage))
|
||||||
|
return false;
|
||||||
|
foreach (const Stash &s, stashes) {
|
||||||
|
if (s.message == message) {
|
||||||
|
*name = s.name;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QString msg = tr("Unable to resolve stash message '%1' in %2").arg(message, workingDirectory);
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool GitClient::synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
|
bool GitClient::synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
|
||||||
QString *output, QString *errorMessage)
|
QString *output, QString *errorMessage)
|
||||||
{
|
{
|
||||||
@@ -631,10 +868,10 @@ bool GitClient::synchronousBranchCmd(const QString &workingDirectory, QStringLis
|
|||||||
QByteArray errorText;
|
QByteArray errorText;
|
||||||
const bool rc = synchronousGit(workingDirectory, branchArgs, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, branchArgs, &outputText, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = tr("Unable to run branch command: %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
*errorMessage = tr("Unable to run branch command: %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
|
*output = commandOutputFromLocal8Bit(outputText);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -649,10 +886,10 @@ bool GitClient::synchronousShow(const QString &workingDirectory, const QString &
|
|||||||
QByteArray errorText;
|
QByteArray errorText;
|
||||||
const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText);
|
const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText);
|
||||||
if (!rc) {
|
if (!rc) {
|
||||||
*errorMessage = tr("Unable to run show: %1: %2").arg(workingDirectory, QString::fromLocal8Bit(errorText));
|
*errorMessage = tr("Unable to run show: %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
*output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
|
*output = commandOutputFromLocal8Bit(outputText);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -805,7 +1042,7 @@ GitClient::StashResult GitClient::ensureStash(const QString &workingDirectory, Q
|
|||||||
case QMessageBox::Cancel:
|
case QMessageBox::Cancel:
|
||||||
return StashCanceled;
|
return StashCanceled;
|
||||||
case QMessageBox::Yes:
|
case QMessageBox::Yes:
|
||||||
if (!synchronousStash(workingDirectory, errorMessage))
|
if (!executeSynchronousStash(workingDirectory, creatorStashMessage(QLatin1String("push")), errorMessage))
|
||||||
return StashFailed;
|
return StashFailed;
|
||||||
break;
|
break;
|
||||||
case QMessageBox::No: // At your own risk, so.
|
case QMessageBox::No: // At your own risk, so.
|
||||||
@@ -845,11 +1082,11 @@ GitClient::StatusResult GitClient::gitStatus(const QString &workingDirectory,
|
|||||||
const bool statusRc = synchronousGit(workingDirectory, statusArgs, &outputText, &errorText);
|
const bool statusRc = synchronousGit(workingDirectory, statusArgs, &outputText, &errorText);
|
||||||
GitCommand::removeColorCodes(&outputText);
|
GitCommand::removeColorCodes(&outputText);
|
||||||
if (output)
|
if (output)
|
||||||
*output = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
|
*output = commandOutputFromLocal8Bit(outputText);
|
||||||
// Is it something really fatal?
|
// Is it something really fatal?
|
||||||
if (!statusRc && !outputText.contains(kBranchIndicatorC)) {
|
if (!statusRc && !outputText.contains(kBranchIndicatorC)) {
|
||||||
if (errorMessage) {
|
if (errorMessage) {
|
||||||
const QString error = QString::fromLocal8Bit(errorText).remove(QLatin1Char('\r'));
|
const QString error = commandOutputFromLocal8Bit(errorText);
|
||||||
*errorMessage = tr("Unable to obtain the status: %1").arg(error);
|
*errorMessage = tr("Unable to obtain the status: %1").arg(error);
|
||||||
}
|
}
|
||||||
return StatusFailed;
|
return StatusFailed;
|
||||||
@@ -890,7 +1127,7 @@ bool GitClient::getCommitData(const QString &workingDirectory,
|
|||||||
if (QFileInfo(descriptionFile).isFile()) {
|
if (QFileInfo(descriptionFile).isFile()) {
|
||||||
QFile file(descriptionFile);
|
QFile file(descriptionFile);
|
||||||
if (file.open(QIODevice::ReadOnly|QIODevice::Text))
|
if (file.open(QIODevice::ReadOnly|QIODevice::Text))
|
||||||
d->panelInfo.description = QString::fromLocal8Bit(file.readAll()).trimmed();
|
d->panelInfo.description = commandOutputFromLocal8Bit(file.readAll()).trimmed();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run status. Note that it has exitcode 1 if there are no added files.
|
// Run status. Note that it has exitcode 1 if there are no added files.
|
||||||
@@ -996,7 +1233,7 @@ bool GitClient::addAndCommit(const QString &repositoryDirectory,
|
|||||||
if (rc) {
|
if (rc) {
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(tr("Committed %n file(s).\n", 0, checkedFiles.size()));
|
VCSBase::VCSBaseOutputWindow::instance()->append(tr("Committed %n file(s).\n", 0, checkedFiles.size()));
|
||||||
} else {
|
} else {
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(QString::fromLocal8Bit(errorText)));
|
VCSBase::VCSBaseOutputWindow::instance()->appendError(tr("Unable to commit %n file(s): %1\n", 0, checkedFiles.size()).arg(commandOutputFromLocal8Bit(errorText)));
|
||||||
}
|
}
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
@@ -1086,7 +1323,7 @@ GitClient::RevertResult GitClient::revertI(QStringList files, bool *ptrToIsDirec
|
|||||||
if (!stagedFiles.empty() && !synchronousReset(repoDirectory, stagedFiles, errorMessage))
|
if (!stagedFiles.empty() && !synchronousReset(repoDirectory, stagedFiles, errorMessage))
|
||||||
return RevertFailed;
|
return RevertFailed;
|
||||||
// Finally revert!
|
// Finally revert!
|
||||||
if (!synchronousCheckout(repoDirectory, stagedFiles + unstagedFiles, errorMessage))
|
if (!synchronousCheckoutFiles(repoDirectory, stagedFiles + unstagedFiles, QString(), errorMessage))
|
||||||
return RevertFailed;
|
return RevertFailed;
|
||||||
return RevertOk;
|
return RevertOk;
|
||||||
}
|
}
|
||||||
@@ -1128,23 +1365,6 @@ QString GitClient::msgNoChangedFiles()
|
|||||||
return tr("There are no modified files.");
|
return tr("There are no modified files.");
|
||||||
}
|
}
|
||||||
|
|
||||||
void GitClient::stash(const QString &workingDirectory)
|
|
||||||
{
|
|
||||||
// Check for changes and stash
|
|
||||||
QString errorMessage;
|
|
||||||
switch (gitStatus(workingDirectory, false, 0, &errorMessage)) {
|
|
||||||
case StatusChanged:
|
|
||||||
executeGit(workingDirectory, QStringList(QLatin1String("stash")), 0, true);
|
|
||||||
break;
|
|
||||||
case StatusUnchanged:
|
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(msgNoChangedFiles());
|
|
||||||
break;
|
|
||||||
case StatusFailed:
|
|
||||||
VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void GitClient::stashPop(const QString &workingDirectory)
|
void GitClient::stashPop(const QString &workingDirectory)
|
||||||
{
|
{
|
||||||
QStringList arguments(QLatin1String("stash"));
|
QStringList arguments(QLatin1String("stash"));
|
||||||
@@ -1153,6 +1373,70 @@ void GitClient::stashPop(const QString &workingDirectory)
|
|||||||
connectRepositoryChanged(workingDirectory, cmd);
|
connectRepositoryChanged(workingDirectory, cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitClient::synchronousStashRestore(const QString &workingDirectory,
|
||||||
|
const QString &stash,
|
||||||
|
const QString &branch /* = QString()*/,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
QStringList arguments(QLatin1String("stash"));
|
||||||
|
if (branch.isEmpty()) {
|
||||||
|
arguments << QLatin1String("apply") << stash;
|
||||||
|
} else {
|
||||||
|
arguments << QLatin1String("branch") << branch << stash;
|
||||||
|
}
|
||||||
|
QByteArray outputText;
|
||||||
|
QByteArray errorText;
|
||||||
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
|
if (!rc) {
|
||||||
|
const QString stdErr = commandOutputFromLocal8Bit(errorText);
|
||||||
|
const QString msg = branch.isEmpty() ?
|
||||||
|
tr("Unable to restore stash %1: %2").arg(workingDirectory, stdErr) :
|
||||||
|
tr("Unable to restore stash %1 to branch %2: %3").arg(workingDirectory, branch, stdErr);
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QString output = commandOutputFromLocal8Bit(outputText);
|
||||||
|
if (!output.isEmpty())
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(output);
|
||||||
|
GitPlugin::instance()->gitVersionControl()->emitRepositoryChanged(workingDirectory);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GitClient::synchronousStashRemove(const QString &workingDirectory,
|
||||||
|
const QString &stash /* = QString() */,
|
||||||
|
QString *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
QStringList arguments(QLatin1String("stash"));
|
||||||
|
if (stash.isEmpty()) {
|
||||||
|
arguments << QLatin1String("clear");
|
||||||
|
} else {
|
||||||
|
arguments << QLatin1String("drop") << stash;
|
||||||
|
}
|
||||||
|
QByteArray outputText;
|
||||||
|
QByteArray errorText;
|
||||||
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
|
if (!rc) {
|
||||||
|
const QString stdErr = commandOutputFromLocal8Bit(errorText);
|
||||||
|
const QString msg = stash.isEmpty() ?
|
||||||
|
tr("Unable to remove stashes of %1: %2").arg(workingDirectory, stdErr) :
|
||||||
|
tr("Unable to remove stash %1 of %2: %3").arg(stash, workingDirectory, stdErr);
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
QString output = commandOutputFromLocal8Bit(outputText);
|
||||||
|
if (!output.isEmpty())
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(output);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GitClient::branchList(const QString &workingDirectory)
|
void GitClient::branchList(const QString &workingDirectory)
|
||||||
{
|
{
|
||||||
QStringList arguments(QLatin1String("branch"));
|
QStringList arguments(QLatin1String("branch"));
|
||||||
@@ -1167,6 +1451,34 @@ void GitClient::stashList(const QString &workingDirectory)
|
|||||||
executeGit(workingDirectory, arguments, 0, true);
|
executeGit(workingDirectory, arguments, 0, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool GitClient::synchronousStashList(const QString &workingDirectory,
|
||||||
|
QList<Stash> *stashes,
|
||||||
|
QString *errorMessage /* = 0 */)
|
||||||
|
{
|
||||||
|
stashes->clear();
|
||||||
|
QStringList arguments(QLatin1String("stash"));
|
||||||
|
arguments << QLatin1String("list") << QLatin1String(noColorOption);
|
||||||
|
QByteArray outputText;
|
||||||
|
QByteArray errorText;
|
||||||
|
const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText);
|
||||||
|
if (!rc) {
|
||||||
|
const QString msg = tr("Unable retrieve stash list of %1: %2").arg(workingDirectory, commandOutputFromLocal8Bit(errorText));
|
||||||
|
if (errorMessage) {
|
||||||
|
*errorMessage = msg;
|
||||||
|
} else {
|
||||||
|
VCSBase::VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Stash stash;
|
||||||
|
foreach(const QString &line, commandOutputLinesFromLocal8Bit(outputText))
|
||||||
|
if (stash.parseStashLine(line))
|
||||||
|
stashes->push_back(stash);
|
||||||
|
if (Git::Constants::debug)
|
||||||
|
qDebug() << Q_FUNC_INFO << *stashes;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar)
|
QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar)
|
||||||
{
|
{
|
||||||
QStringList arguments;
|
QStringList arguments;
|
||||||
@@ -1174,7 +1486,7 @@ QString GitClient::readConfig(const QString &workingDirectory, const QStringList
|
|||||||
|
|
||||||
QByteArray outputText;
|
QByteArray outputText;
|
||||||
if (synchronousGit(workingDirectory, arguments, &outputText, 0, false))
|
if (synchronousGit(workingDirectory, arguments, &outputText, 0, false))
|
||||||
return QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r'));
|
return commandOutputFromLocal8Bit(outputText);
|
||||||
return QString();
|
return QString();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1211,3 +1523,6 @@ void GitClient::connectRepositoryChanged(const QString & repository, GitCommand
|
|||||||
connect(cmd, SIGNAL(success()), m_repositoryChangedSignalMapper, SLOT(map()),
|
connect(cmd, SIGNAL(success()), m_repositoryChangedSignalMapper, SLOT(map()),
|
||||||
Qt::QueuedConnection);
|
Qt::QueuedConnection);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -41,6 +41,7 @@
|
|||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QErrorMessage;
|
class QErrorMessage;
|
||||||
class QSignalMapper;
|
class QSignalMapper;
|
||||||
|
class QDebug;
|
||||||
QT_END_NAMESPACE
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Core {
|
namespace Core {
|
||||||
@@ -59,12 +60,15 @@ class GitOutputWindow;
|
|||||||
class GitCommand;
|
class GitCommand;
|
||||||
struct CommitData;
|
struct CommitData;
|
||||||
struct GitSubmitEditorPanelData;
|
struct GitSubmitEditorPanelData;
|
||||||
|
struct Stash;
|
||||||
|
|
||||||
class GitClient : public QObject
|
class GitClient : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
static const char *stashNamePrefix;
|
||||||
|
|
||||||
explicit GitClient(GitPlugin *plugin);
|
explicit GitClient(GitPlugin *plugin);
|
||||||
~GitClient();
|
~GitClient();
|
||||||
|
|
||||||
@@ -84,17 +88,37 @@ public:
|
|||||||
bool enableAnnotationContextMenu = false);
|
bool enableAnnotationContextMenu = false);
|
||||||
void blame(const QString &workingDirectory, const QString &fileName,
|
void blame(const QString &workingDirectory, const QString &fileName,
|
||||||
const QString &revision = QString(), int lineNumber = -1);
|
const QString &revision = QString(), int lineNumber = -1);
|
||||||
void showCommit(const QString &workingDirectory, const QString &commit);
|
|
||||||
void checkout(const QString &workingDirectory, const QString &file);
|
void checkout(const QString &workingDirectory, const QString &file);
|
||||||
void checkoutBranch(const QString &workingDirectory, const QString &branch);
|
void checkoutBranch(const QString &workingDirectory, const QString &branch);
|
||||||
void hardReset(const QString &workingDirectory, const QString &commit);
|
void hardReset(const QString &workingDirectory, const QString &commit = QString());
|
||||||
void addFile(const QString &workingDirectory, const QString &fileName);
|
void addFile(const QString &workingDirectory, const QString &fileName);
|
||||||
bool synchronousAdd(const QString &workingDirectory, const QStringList &files);
|
bool synchronousAdd(const QString &workingDirectory, const QStringList &files);
|
||||||
bool synchronousReset(const QString &workingDirectory, const QStringList &files);
|
bool synchronousReset(const QString &workingDirectory,
|
||||||
bool synchronousReset(const QString &workingDirectory, const QStringList &files, QString *errorMessage);
|
const QStringList &files = QStringList(),
|
||||||
|
QString *errorMessage = 0);
|
||||||
bool synchronousInit(const QString &workingDirectory);
|
bool synchronousInit(const QString &workingDirectory);
|
||||||
bool synchronousCheckout(const QString &workingDirectory, const QStringList &files, QString *errorMessage);
|
bool synchronousCheckoutFiles(const QString &workingDirectory,
|
||||||
bool synchronousStash(const QString &workingDirectory, QString *errorMessage);
|
QStringList files = QStringList(),
|
||||||
|
QString revision = QString(), QString *errorMessage = 0);
|
||||||
|
// Checkout branch
|
||||||
|
bool synchronousCheckoutBranch(const QString &workingDirectory, const QString &branch, QString *errorMessage = 0);
|
||||||
|
|
||||||
|
// Do a stash and return identier.
|
||||||
|
enum { StashPromptDescription = 0x1, StashImmediateRestore = 0x2, StashIgnoreUnchanged = 0x4 };
|
||||||
|
QString synchronousStash(const QString &workingDirectory,
|
||||||
|
const QString &messageKeyword = QString(),
|
||||||
|
unsigned flags = 0, bool *unchanged = 0);
|
||||||
|
|
||||||
|
bool executeSynchronousStash(const QString &workingDirectory,
|
||||||
|
const QString &message = QString(),
|
||||||
|
QString *errorMessage = 0);
|
||||||
|
bool synchronousStashRestore(const QString &workingDirectory,
|
||||||
|
const QString &stash,
|
||||||
|
const QString &branch = QString(),
|
||||||
|
QString *errorMessage = 0);
|
||||||
|
bool synchronousStashRemove(const QString &workingDirectory,
|
||||||
|
const QString &stash = QString(),
|
||||||
|
QString *errorMessage = 0);
|
||||||
bool synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
|
bool synchronousBranchCmd(const QString &workingDirectory, QStringList branchArgs,
|
||||||
QString *output, QString *errorMessage);
|
QString *output, QString *errorMessage);
|
||||||
bool synchronousShow(const QString &workingDirectory, const QString &id,
|
bool synchronousShow(const QString &workingDirectory, const QString &id,
|
||||||
@@ -110,15 +134,23 @@ public:
|
|||||||
const QString &format, QString *description, QString *errorMessage);
|
const QString &format, QString *description, QString *errorMessage);
|
||||||
bool synchronousShortDescriptions(const QString &workingDirectory, const QStringList &revisions,
|
bool synchronousShortDescriptions(const QString &workingDirectory, const QStringList &revisions,
|
||||||
QStringList *descriptions, QString *errorMessage);
|
QStringList *descriptions, QString *errorMessage);
|
||||||
|
bool synchronousTopRevision(const QString &workingDirectory, QString *revision = 0,
|
||||||
|
QString *branch = 0, QString *errorMessage = 0);
|
||||||
|
|
||||||
void pull(const QString &workingDirectory);
|
void pull(const QString &workingDirectory);
|
||||||
void push(const QString &workingDirectory);
|
void push(const QString &workingDirectory);
|
||||||
|
|
||||||
void stash(const QString &workingDirectory);
|
|
||||||
void stashPop(const QString &workingDirectory);
|
void stashPop(const QString &workingDirectory);
|
||||||
void revert(const QStringList &files);
|
void revert(const QStringList &files);
|
||||||
void branchList(const QString &workingDirectory);
|
void branchList(const QString &workingDirectory);
|
||||||
void stashList(const QString &workingDirectory);
|
void stashList(const QString &workingDirectory);
|
||||||
|
bool synchronousStashList(const QString &workingDirectory,
|
||||||
|
QList<Stash> *stashes,
|
||||||
|
QString *errorMessage = 0);
|
||||||
|
// Resolve a stash name from message (for IVersionControl's names).
|
||||||
|
bool stashNameFromMessage(const QString &workingDirectory,
|
||||||
|
const QString &messge, QString *name,
|
||||||
|
QString *errorMessage = 0);
|
||||||
|
|
||||||
QString readConfig(const QString &workingDirectory, const QStringList &configVar);
|
QString readConfig(const QString &workingDirectory, const QStringList &configVar);
|
||||||
|
|
||||||
|
@@ -39,6 +39,7 @@
|
|||||||
#include "branchdialog.h"
|
#include "branchdialog.h"
|
||||||
#include "clonewizard.h"
|
#include "clonewizard.h"
|
||||||
#include "gitoriousclonewizard.h"
|
#include "gitoriousclonewizard.h"
|
||||||
|
#include "stashdialog.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
#include <coreplugin/coreconstants.h>
|
#include <coreplugin/coreconstants.h>
|
||||||
@@ -134,6 +135,7 @@ GitPlugin::GitPlugin() :
|
|||||||
m_undoAction(0),
|
m_undoAction(0),
|
||||||
m_redoAction(0),
|
m_redoAction(0),
|
||||||
m_stashAction(0),
|
m_stashAction(0),
|
||||||
|
m_stashSnapshotAction(0),
|
||||||
m_stashPopAction(0),
|
m_stashPopAction(0),
|
||||||
m_stashListAction(0),
|
m_stashListAction(0),
|
||||||
m_branchListAction(0),
|
m_branchListAction(0),
|
||||||
@@ -314,36 +316,37 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
|||||||
|
|
||||||
gitContainer->addAction(createSeparator(actionManager, globalcontext, QLatin1String("Git.Sep.Global"), this));
|
gitContainer->addAction(createSeparator(actionManager, globalcontext, QLatin1String("Git.Sep.Global"), this));
|
||||||
|
|
||||||
|
m_stashSnapshotAction = new QAction(tr("Stash snapshot..."), this);
|
||||||
|
m_stashSnapshotAction->setToolTip(tr("Saves the current state of your work."));
|
||||||
|
command = actionManager->registerAction(m_stashSnapshotAction, "Git.StashSnapshot", globalcontext);
|
||||||
|
connect(m_stashSnapshotAction, SIGNAL(triggered()), this, SLOT(stashSnapshot()));
|
||||||
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_stashAction = new QAction(tr("Stash"), this);
|
m_stashAction = new QAction(tr("Stash"), this);
|
||||||
m_stashAction->setToolTip(tr("Saves the current state of your work."));
|
m_stashAction->setToolTip(tr("Saves the current state of your work and resets the repository."));
|
||||||
command = actionManager->registerAction(m_stashAction, "Git.Stash", globalcontext);
|
command = actionManager->registerAction(m_stashAction, "Git.Stash", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_stashAction, SIGNAL(triggered()), this, SLOT(stash()));
|
connect(m_stashAction, SIGNAL(triggered()), this, SLOT(stash()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_pullAction = new QAction(tr("Pull"), this);
|
m_pullAction = new QAction(tr("Pull"), this);
|
||||||
command = actionManager->registerAction(m_pullAction, "Git.Pull", globalcontext);
|
command = actionManager->registerAction(m_pullAction, "Git.Pull", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_pullAction, SIGNAL(triggered()), this, SLOT(pull()));
|
connect(m_pullAction, SIGNAL(triggered()), this, SLOT(pull()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_stashPopAction = new QAction(tr("Stash Pop"), this);
|
m_stashPopAction = new QAction(tr("Stash Pop"), this);
|
||||||
m_stashAction->setToolTip(tr("Restores changes saved to the stash list using \"Stash\"."));
|
m_stashAction->setToolTip(tr("Restores changes saved to the stash list using \"Stash\"."));
|
||||||
command = actionManager->registerAction(m_stashPopAction, "Git.StashPop", globalcontext);
|
command = actionManager->registerAction(m_stashPopAction, "Git.StashPop", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_stashPopAction, SIGNAL(triggered()), this, SLOT(stashPop()));
|
connect(m_stashPopAction, SIGNAL(triggered()), this, SLOT(stashPop()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_commitAction = new QAction(tr("Commit..."), this);
|
m_commitAction = new QAction(tr("Commit..."), this);
|
||||||
command = actionManager->registerAction(m_commitAction, "Git.Commit", globalcontext);
|
command = actionManager->registerAction(m_commitAction, "Git.Commit", globalcontext);
|
||||||
command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C")));
|
command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C")));
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_commitAction, SIGNAL(triggered()), this, SLOT(startCommit()));
|
connect(m_commitAction, SIGNAL(triggered()), this, SLOT(startCommit()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_pushAction = new QAction(tr("Push"), this);
|
m_pushAction = new QAction(tr("Push"), this);
|
||||||
command = actionManager->registerAction(m_pushAction, "Git.Push", globalcontext);
|
command = actionManager->registerAction(m_pushAction, "Git.Push", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_pushAction, SIGNAL(triggered()), this, SLOT(push()));
|
connect(m_pushAction, SIGNAL(triggered()), this, SLOT(push()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
@@ -351,22 +354,30 @@ bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
|
|||||||
|
|
||||||
m_branchListAction = new QAction(tr("Branches..."), this);
|
m_branchListAction = new QAction(tr("Branches..."), this);
|
||||||
command = actionManager->registerAction(m_branchListAction, "Git.BranchList", globalcontext);
|
command = actionManager->registerAction(m_branchListAction, "Git.BranchList", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_branchListAction, SIGNAL(triggered()), this, SLOT(branchList()));
|
connect(m_branchListAction, SIGNAL(triggered()), this, SLOT(branchList()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_stashListAction = new QAction(tr("List Stashes"), this);
|
m_stashListAction = new QAction(tr("Stashes..."), this);
|
||||||
command = actionManager->registerAction(m_stashListAction, "Git.StashList", globalcontext);
|
command = actionManager->registerAction(m_stashListAction, "Git.StashList", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_stashListAction, SIGNAL(triggered()), this, SLOT(stashList()));
|
connect(m_stashListAction, SIGNAL(triggered()), this, SLOT(stashList()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
m_showAction = new QAction(tr("Show Commit..."), this);
|
m_showAction = new QAction(tr("Show Commit..."), this);
|
||||||
command = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext);
|
command = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext);
|
||||||
command->setAttribute(Core::Command::CA_UpdateText);
|
|
||||||
connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
|
connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
|
||||||
gitContainer->addAction(command);
|
gitContainer->addAction(command);
|
||||||
|
|
||||||
|
if (0) {
|
||||||
|
const QList<QAction*> snapShotActions = createSnapShotTestActions();
|
||||||
|
const int count = snapShotActions.size();
|
||||||
|
for (int i = 0; i < count; i++) {
|
||||||
|
command = actionManager->registerAction(snapShotActions.at(i),
|
||||||
|
QLatin1String("Git.Snapshot.") + QString::number(i),
|
||||||
|
globalcontext);
|
||||||
|
gitContainer->addAction(command);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Submit editor
|
// Submit editor
|
||||||
QList<int> submitContext;
|
QList<int> submitContext;
|
||||||
submitContext.push_back(m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Constants::C_GITSUBMITEDITOR)));
|
submitContext.push_back(m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Constants::C_GITSUBMITEDITOR)));
|
||||||
@@ -648,9 +659,22 @@ void GitPlugin::push()
|
|||||||
|
|
||||||
void GitPlugin::stash()
|
void GitPlugin::stash()
|
||||||
{
|
{
|
||||||
|
// Simple stash without prompt, reset repo.
|
||||||
const VCSBase::VCSBasePluginState state = currentState();
|
const VCSBase::VCSBasePluginState state = currentState();
|
||||||
QTC_ASSERT(state.hasTopLevel(), return)
|
QTC_ASSERT(state.hasTopLevel(), return)
|
||||||
m_gitClient->stash(state.topLevel());
|
const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), 0);
|
||||||
|
if (!id.isEmpty() && m_stashDialog)
|
||||||
|
m_stashDialog->refresh(state.topLevel(), true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GitPlugin::stashSnapshot()
|
||||||
|
{
|
||||||
|
// Prompt for description, restore immediately and keep on working.
|
||||||
|
const VCSBase::VCSBasePluginState state = currentState();
|
||||||
|
QTC_ASSERT(state.hasTopLevel(), return)
|
||||||
|
const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), GitClient::StashImmediateRestore|GitClient::StashPromptDescription);
|
||||||
|
if (!id.isEmpty() && m_stashDialog)
|
||||||
|
m_stashDialog->refresh(state.topLevel(), true);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GitPlugin::stashPop()
|
void GitPlugin::stashPop()
|
||||||
@@ -676,9 +700,15 @@ void GitPlugin::branchList()
|
|||||||
|
|
||||||
void GitPlugin::stashList()
|
void GitPlugin::stashList()
|
||||||
{
|
{
|
||||||
const VCSBase::VCSBasePluginState state = currentState();
|
// Raise non-modal stash dialog.
|
||||||
QTC_ASSERT(state.hasTopLevel(), return)
|
if (m_stashDialog) {
|
||||||
m_gitClient->stashList(state.topLevel());
|
m_stashDialog->show();
|
||||||
|
m_stashDialog->raise();
|
||||||
|
} else {
|
||||||
|
m_stashDialog = new StashDialog(Core::ICore::instance()->mainWindow());
|
||||||
|
m_stashDialog->refresh(currentState().topLevel(), true);
|
||||||
|
m_stashDialog->show();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
|
void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
|
||||||
@@ -716,6 +746,7 @@ void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
|
|||||||
m_branchListAction->setEnabled(repositoryEnabled);
|
m_branchListAction->setEnabled(repositoryEnabled);
|
||||||
m_stashListAction->setEnabled(repositoryEnabled);
|
m_stashListAction->setEnabled(repositoryEnabled);
|
||||||
m_stashAction->setEnabled(repositoryEnabled);
|
m_stashAction->setEnabled(repositoryEnabled);
|
||||||
|
m_stashSnapshotAction->setEnabled(repositoryEnabled);
|
||||||
m_pullAction->setEnabled(repositoryEnabled);
|
m_pullAction->setEnabled(repositoryEnabled);
|
||||||
m_commitAction->setEnabled(repositoryEnabled);
|
m_commitAction->setEnabled(repositoryEnabled);
|
||||||
m_stashPopAction->setEnabled(repositoryEnabled);
|
m_stashPopAction->setEnabled(repositoryEnabled);
|
||||||
@@ -723,6 +754,9 @@ void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
|
|||||||
m_undoRepositoryAction->setEnabled(repositoryEnabled);
|
m_undoRepositoryAction->setEnabled(repositoryEnabled);
|
||||||
m_pushAction->setEnabled(repositoryEnabled);
|
m_pushAction->setEnabled(repositoryEnabled);
|
||||||
|
|
||||||
|
if (m_stashDialog)
|
||||||
|
m_stashDialog->refresh(currentState().topLevel(), false);
|
||||||
|
|
||||||
// Prompts for repo.
|
// Prompts for repo.
|
||||||
m_showAction->setEnabled(true);
|
m_showAction->setEnabled(true);
|
||||||
}
|
}
|
||||||
|
@@ -39,6 +39,7 @@
|
|||||||
#include <QtCore/QObject>
|
#include <QtCore/QObject>
|
||||||
#include <QtCore/QProcess>
|
#include <QtCore/QProcess>
|
||||||
#include <QtCore/QStringList>
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtCore/QPointer>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QFile;
|
class QFile;
|
||||||
@@ -64,6 +65,7 @@ class ChangeSelectionDialog;
|
|||||||
class GitSubmitEditor;
|
class GitSubmitEditor;
|
||||||
struct CommitData;
|
struct CommitData;
|
||||||
struct GitSettings;
|
struct GitSettings;
|
||||||
|
class StashDialog;
|
||||||
|
|
||||||
class GitPlugin : public VCSBase::VCSBasePlugin
|
class GitPlugin : public VCSBase::VCSBasePlugin
|
||||||
{
|
{
|
||||||
@@ -104,6 +106,7 @@ private slots:
|
|||||||
void showCommit();
|
void showCommit();
|
||||||
void startCommit();
|
void startCommit();
|
||||||
void stash();
|
void stash();
|
||||||
|
void stashSnapshot();
|
||||||
void stashPop();
|
void stashPop();
|
||||||
void branchList();
|
void branchList();
|
||||||
void stashList();
|
void stashList();
|
||||||
@@ -145,6 +148,7 @@ private:
|
|||||||
QAction *m_undoAction;
|
QAction *m_undoAction;
|
||||||
QAction *m_redoAction;
|
QAction *m_redoAction;
|
||||||
QAction *m_stashAction;
|
QAction *m_stashAction;
|
||||||
|
QAction *m_stashSnapshotAction;
|
||||||
QAction *m_stashPopAction;
|
QAction *m_stashPopAction;
|
||||||
QAction *m_stashListAction;
|
QAction *m_stashListAction;
|
||||||
QAction *m_branchListAction;
|
QAction *m_branchListAction;
|
||||||
@@ -152,11 +156,13 @@ private:
|
|||||||
|
|
||||||
GitClient *m_gitClient;
|
GitClient *m_gitClient;
|
||||||
ChangeSelectionDialog *m_changeSelectionDialog;
|
ChangeSelectionDialog *m_changeSelectionDialog;
|
||||||
|
QPointer<StashDialog> m_stashDialog;
|
||||||
QString m_submitRepository;
|
QString m_submitRepository;
|
||||||
QStringList m_submitOrigCommitFiles;
|
QStringList m_submitOrigCommitFiles;
|
||||||
QStringList m_submitOrigDeleteFiles;
|
QStringList m_submitOrigDeleteFiles;
|
||||||
QString m_commitMessageFileName;
|
QString m_commitMessageFileName;
|
||||||
bool m_submitActionTriggered;
|
bool m_submitActionTriggered;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace Git
|
} // namespace Git
|
||||||
|
104
src/plugins/git/gitutils.cpp
Normal file
104
src/plugins/git/gitutils.cpp
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "gitutils.h"
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QStringList>
|
||||||
|
#include <QtGui/QInputDialog>
|
||||||
|
#include <QtGui/QLineEdit>
|
||||||
|
|
||||||
|
namespace Git {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug d, const Stash &s)
|
||||||
|
{
|
||||||
|
QDebug nospace = d.nospace();
|
||||||
|
nospace << "name=" << s.name << " branch=" << s.branch << " message=" << s.message;
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Stash::clear()
|
||||||
|
{
|
||||||
|
name.clear();
|
||||||
|
branch.clear();
|
||||||
|
message.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Parse a stash line in its 2 manifestations (with message/without message containing
|
||||||
|
* <base_sha1>+subject):
|
||||||
|
\code
|
||||||
|
stash@{1}: WIP on <branch>: <base_sha1> <subject_base_sha1>
|
||||||
|
stash@{2}: On <branch>: <message>
|
||||||
|
\endcode */
|
||||||
|
|
||||||
|
bool Stash::parseStashLine(const QString &l)
|
||||||
|
{
|
||||||
|
const QChar colon = QLatin1Char(':');
|
||||||
|
const int branchPos = l.indexOf(colon);
|
||||||
|
if (branchPos < 0)
|
||||||
|
return false;
|
||||||
|
const int messagePos = l.indexOf(colon, branchPos + 1);
|
||||||
|
if (messagePos < 0)
|
||||||
|
return false;
|
||||||
|
// Name
|
||||||
|
const QString newName = l.left(branchPos);
|
||||||
|
// Branch spec
|
||||||
|
const QString branchSpec = l.mid(branchPos + 1, messagePos - branchPos - 1);
|
||||||
|
const bool emptyMessage = branchSpec.contains(QLatin1String("WIP")); // "Work in Progress or sth"
|
||||||
|
const int onIndex = branchSpec.indexOf(QLatin1String("on "), 0, Qt::CaseInsensitive);
|
||||||
|
if (onIndex == -1)
|
||||||
|
return false;
|
||||||
|
const QString newBranch = branchSpec.mid(onIndex + 3);
|
||||||
|
// Happy!
|
||||||
|
name = newName;
|
||||||
|
branch = newBranch;
|
||||||
|
if (!emptyMessage)
|
||||||
|
message = l.mid(messagePos + 2); // skip blank
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make QInputDialog play nicely, widen it a bit.
|
||||||
|
bool inputText(QWidget *parent, const QString &title, const QString &prompt, QString *s)
|
||||||
|
{
|
||||||
|
QInputDialog dialog(parent);
|
||||||
|
dialog.setWindowFlags(dialog.windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
dialog.setWindowTitle(title);
|
||||||
|
dialog.setLabelText(prompt);
|
||||||
|
dialog.setTextValue(*s);
|
||||||
|
// Nasty hack:
|
||||||
|
if (QLineEdit *le = qFindChild<QLineEdit*>(&dialog))
|
||||||
|
le->setMinimumWidth(500);
|
||||||
|
if (dialog.exec() != QDialog::Accepted)
|
||||||
|
return false;
|
||||||
|
*s = dialog.textValue();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Git
|
60
src/plugins/git/gitutils.h
Normal file
60
src/plugins/git/gitutils.h
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef GITUTILS_H
|
||||||
|
#define GITUTILS_H
|
||||||
|
|
||||||
|
#include <QtCore/QString>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QDebug;
|
||||||
|
class QWidget;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Git {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
struct Stash {
|
||||||
|
void clear();
|
||||||
|
bool parseStashLine(const QString &l);
|
||||||
|
|
||||||
|
QString name;
|
||||||
|
QString branch;
|
||||||
|
QString message;
|
||||||
|
};
|
||||||
|
|
||||||
|
QDebug operator<<(QDebug d, const Stash &);
|
||||||
|
|
||||||
|
// Make QInputDialog play nicely
|
||||||
|
bool inputText(QWidget *parent, const QString &title, const QString &prompt, QString *s);
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Git
|
||||||
|
|
||||||
|
#endif // GITUTILS_H
|
@@ -30,10 +30,19 @@
|
|||||||
#include "gitversioncontrol.h"
|
#include "gitversioncontrol.h"
|
||||||
#include "gitclient.h"
|
#include "gitclient.h"
|
||||||
#include "gitplugin.h"
|
#include "gitplugin.h"
|
||||||
|
#include "gitutils.h"
|
||||||
|
|
||||||
|
static const char stashMessageKeywordC[] = "IVersionControl@";
|
||||||
|
static const char stashRevisionIdC[] = "revision";
|
||||||
|
|
||||||
namespace Git {
|
namespace Git {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
|
static inline GitClient *gitClient()
|
||||||
|
{
|
||||||
|
return GitPlugin::instance()->gitClient();
|
||||||
|
}
|
||||||
|
|
||||||
GitVersionControl::GitVersionControl(GitClient *client) :
|
GitVersionControl::GitVersionControl(GitClient *client) :
|
||||||
m_enabled(true),
|
m_enabled(true),
|
||||||
m_client(client)
|
m_client(client)
|
||||||
@@ -54,6 +63,7 @@ bool GitVersionControl::supportsOperation(Operation operation) const
|
|||||||
case OpenOperation:
|
case OpenOperation:
|
||||||
break;
|
break;
|
||||||
case CreateRepositoryOperation:
|
case CreateRepositoryOperation:
|
||||||
|
case SnapshotOperations:
|
||||||
rc = true;
|
rc = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -78,7 +88,91 @@ bool GitVersionControl::vcsDelete(const QString & /*fileName*/)
|
|||||||
|
|
||||||
bool GitVersionControl::vcsCreateRepository(const QString &directory)
|
bool GitVersionControl::vcsCreateRepository(const QString &directory)
|
||||||
{
|
{
|
||||||
return GitPlugin::instance()->gitClient()->synchronousInit(directory);
|
return gitClient()->synchronousInit(directory);
|
||||||
|
}
|
||||||
|
/* Snapshots are implement using stashes, relying on stash messages for
|
||||||
|
* naming as the actual stash names (stash{n}) are rotated as one adds stashes.
|
||||||
|
* Note that the snapshot interface does not care whether we have an unmodified
|
||||||
|
* repository state, in which case git refuses to stash.
|
||||||
|
* In that case, return a special identifier as "specialprefix:<branch>:<head revision>",
|
||||||
|
* which will trigger a checkout in restore(). */
|
||||||
|
|
||||||
|
QString GitVersionControl::vcsCreateSnapshot(const QString &topLevel)
|
||||||
|
{
|
||||||
|
bool repositoryUnchanged;
|
||||||
|
// Create unique keyword
|
||||||
|
static int n = 1;
|
||||||
|
QString keyword = QLatin1String(stashMessageKeywordC) + QString::number(n++);
|
||||||
|
const QString stashMessage =
|
||||||
|
gitClient()->synchronousStash(topLevel, keyword,
|
||||||
|
GitClient::StashImmediateRestore|GitClient::StashIgnoreUnchanged,
|
||||||
|
&repositoryUnchanged);
|
||||||
|
if (!stashMessage.isEmpty())
|
||||||
|
return stashMessage;
|
||||||
|
if (repositoryUnchanged) {
|
||||||
|
// For unchanged repository state: return identifier + top revision
|
||||||
|
QString topRevision;
|
||||||
|
QString branch;
|
||||||
|
if (!gitClient()->synchronousTopRevision(topLevel, &topRevision, &branch))
|
||||||
|
return QString();
|
||||||
|
const QChar colon = QLatin1Char(':');
|
||||||
|
QString id = QLatin1String(stashRevisionIdC);
|
||||||
|
id += colon;
|
||||||
|
id += branch;
|
||||||
|
id += colon;
|
||||||
|
id += topRevision;
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
return QString(); // Failure
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList GitVersionControl::vcsSnapshots(const QString &topLevel)
|
||||||
|
{
|
||||||
|
QList<Stash> stashes;
|
||||||
|
if (!gitClient()->synchronousStashList(topLevel, &stashes))
|
||||||
|
return QStringList();
|
||||||
|
// Return the git stash 'message' as identifier, ignoring empty ones
|
||||||
|
QStringList rc;
|
||||||
|
foreach(const Stash &s, stashes)
|
||||||
|
if (!s.message.isEmpty())
|
||||||
|
rc.push_back(s.message);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GitVersionControl::vcsRestoreSnapshot(const QString &topLevel, const QString &name)
|
||||||
|
{
|
||||||
|
bool success = false;
|
||||||
|
do {
|
||||||
|
// Is this a revision or a stash
|
||||||
|
if (name.startsWith(QLatin1String(stashRevisionIdC))) {
|
||||||
|
// Restore "id:branch:revision"
|
||||||
|
const QStringList tokens = name.split(QLatin1Char(':'));
|
||||||
|
if (tokens.size() != 3)
|
||||||
|
break;
|
||||||
|
const QString branch = tokens.at(1);
|
||||||
|
const QString revision = tokens.at(2);
|
||||||
|
success = gitClient()->synchronousReset(topLevel)
|
||||||
|
&& gitClient()->synchronousCheckoutBranch(topLevel, branch)
|
||||||
|
&& gitClient()->synchronousCheckoutFiles(topLevel, QStringList(), revision);
|
||||||
|
} else {
|
||||||
|
// Restore stash if it can be resolved.
|
||||||
|
QString stashName;
|
||||||
|
success = gitClient()->stashNameFromMessage(topLevel, name, &stashName)
|
||||||
|
&& gitClient()->synchronousReset(topLevel)
|
||||||
|
&& gitClient()->synchronousStashRestore(topLevel, stashName);
|
||||||
|
}
|
||||||
|
} while (false);
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GitVersionControl::vcsRemoveSnapshot(const QString &topLevel, const QString &name)
|
||||||
|
{
|
||||||
|
// Is this a revision -> happy
|
||||||
|
if (name.startsWith(QLatin1String(stashRevisionIdC)))
|
||||||
|
return true;
|
||||||
|
QString stashName;
|
||||||
|
return gitClient()->stashNameFromMessage(topLevel, name, &stashName)
|
||||||
|
&& gitClient()->synchronousStashRemove(topLevel, stashName);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GitVersionControl::managesDirectory(const QString &directory) const
|
bool GitVersionControl::managesDirectory(const QString &directory) const
|
||||||
@@ -96,5 +190,10 @@ void GitVersionControl::emitFilesChanged(const QStringList &l)
|
|||||||
emit filesChanged(l);
|
emit filesChanged(l);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GitVersionControl::emitRepositoryChanged(const QString &r)
|
||||||
|
{
|
||||||
|
emit repositoryChanged(r);
|
||||||
|
}
|
||||||
|
|
||||||
} // Internal
|
} // Internal
|
||||||
} // Git
|
} // Git
|
||||||
|
@@ -54,11 +54,13 @@ public:
|
|||||||
virtual bool vcsAdd(const QString &fileName);
|
virtual bool vcsAdd(const QString &fileName);
|
||||||
virtual bool vcsDelete(const QString &filename);
|
virtual bool vcsDelete(const QString &filename);
|
||||||
virtual bool vcsCreateRepository(const QString &directory);
|
virtual bool vcsCreateRepository(const QString &directory);
|
||||||
|
virtual QString vcsCreateSnapshot(const QString &topLevel);
|
||||||
|
virtual QStringList vcsSnapshots(const QString &topLevel);
|
||||||
|
virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
|
||||||
void emitFilesChanged(const QStringList &);
|
void emitFilesChanged(const QStringList &);
|
||||||
|
void emitRepositoryChanged(const QString &);
|
||||||
signals:
|
|
||||||
void enabledChanged(bool);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_enabled;
|
bool m_enabled;
|
||||||
|
411
src/plugins/git/stashdialog.cpp
Normal file
411
src/plugins/git/stashdialog.cpp
Normal file
@@ -0,0 +1,411 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#include "stashdialog.h"
|
||||||
|
#include "gitclient.h"
|
||||||
|
#include "gitplugin.h"
|
||||||
|
#include "gitutils.h"
|
||||||
|
#include "ui_stashdialog.h"
|
||||||
|
|
||||||
|
#include <utils/qtcassert.h>
|
||||||
|
#include <vcsbase/vcsbaseoutputwindow.h>
|
||||||
|
|
||||||
|
#include <QtCore/QDebug>
|
||||||
|
#include <QtCore/QModelIndex>
|
||||||
|
#include <QtCore/QDateTime>
|
||||||
|
#include <QtGui/QStandardItemModel>
|
||||||
|
#include <QtGui/QSortFilterProxyModel>
|
||||||
|
#include <QtGui/QItemSelectionModel>
|
||||||
|
#include <QtGui/QMessageBox>
|
||||||
|
#include <QtGui/QPushButton>
|
||||||
|
|
||||||
|
enum { NameColumn, BranchColumn, MessageColumn, ColumnCount };
|
||||||
|
|
||||||
|
namespace Git {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
static inline GitClient *gitClient()
|
||||||
|
{
|
||||||
|
return GitPlugin::instance()->gitClient();
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QList<QStandardItem*> stashModelRowItems(const Stash &s)
|
||||||
|
{
|
||||||
|
Qt::ItemFlags itemFlags = Qt::ItemIsSelectable|Qt::ItemIsEnabled;
|
||||||
|
QStandardItem *nameItem = new QStandardItem(s.name);
|
||||||
|
nameItem->setFlags(itemFlags);
|
||||||
|
QStandardItem *branchItem = new QStandardItem(s.branch);
|
||||||
|
branchItem->setFlags(itemFlags);
|
||||||
|
QStandardItem *messageItem = new QStandardItem(s.message);
|
||||||
|
messageItem->setFlags(itemFlags);
|
||||||
|
QList<QStandardItem*> rc;
|
||||||
|
rc << nameItem << branchItem << messageItem;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----------- StashModel
|
||||||
|
class StashModel : public QStandardItemModel {
|
||||||
|
public:
|
||||||
|
explicit StashModel(QObject *parent = 0);
|
||||||
|
|
||||||
|
void setStashes(const QList<Stash> &stashes);
|
||||||
|
const Stash &at(int i) { return m_stashes.at(i); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
QList<Stash> m_stashes;
|
||||||
|
};
|
||||||
|
|
||||||
|
StashModel::StashModel(QObject *parent) :
|
||||||
|
QStandardItemModel(0, ColumnCount, parent)
|
||||||
|
{
|
||||||
|
QStringList headers;
|
||||||
|
headers << StashDialog::tr("Name") << StashDialog::tr("Branch") << StashDialog::tr("Message");
|
||||||
|
setHorizontalHeaderLabels(headers);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashModel::setStashes(const QList<Stash> &stashes)
|
||||||
|
{
|
||||||
|
m_stashes = stashes;
|
||||||
|
if (const int rows = rowCount())
|
||||||
|
removeRows(0, rows);
|
||||||
|
foreach(const Stash &s, stashes)
|
||||||
|
appendRow(stashModelRowItems(s));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- StashDialog
|
||||||
|
StashDialog::StashDialog(QWidget *parent) :
|
||||||
|
QDialog(parent),
|
||||||
|
ui(new Ui::StashDialog),
|
||||||
|
m_model(new StashModel),
|
||||||
|
m_proxyModel(new QSortFilterProxyModel),
|
||||||
|
m_deleteAllButton(new QPushButton(tr("Delete all..."))),
|
||||||
|
m_deleteSelectionButton(new QPushButton(tr("Delete..."))),
|
||||||
|
m_showCurrentButton(new QPushButton(tr("Show"))),
|
||||||
|
m_restoreCurrentButton(new QPushButton(tr("Restore..."))),
|
||||||
|
m_restoreCurrentInBranchButton(new QPushButton(tr("Restore to branch..."))),
|
||||||
|
m_refreshButton(new QPushButton(tr("Refresh")))
|
||||||
|
{
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
|
||||||
|
ui->setupUi(this);
|
||||||
|
// Buttons
|
||||||
|
ui->buttonBox->addButton(m_showCurrentButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_showCurrentButton, SIGNAL(clicked()), this, SLOT(showCurrent()));
|
||||||
|
ui->buttonBox->addButton(m_refreshButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_refreshButton, SIGNAL(clicked()), this, SLOT(forceRefresh()));
|
||||||
|
ui->buttonBox->addButton(m_restoreCurrentButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_restoreCurrentButton, SIGNAL(clicked()), this, SLOT(restoreCurrent()));
|
||||||
|
ui->buttonBox->addButton(m_restoreCurrentInBranchButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_restoreCurrentInBranchButton, SIGNAL(clicked()), this, SLOT(restoreCurrentInBranch()));
|
||||||
|
ui->buttonBox->addButton(m_deleteSelectionButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_deleteSelectionButton, SIGNAL(clicked()), this, SLOT(deleteSelection()));
|
||||||
|
ui->buttonBox->addButton(m_deleteAllButton, QDialogButtonBox::ActionRole);
|
||||||
|
connect(m_deleteAllButton, SIGNAL(clicked()), this, SLOT(deleteAll()));
|
||||||
|
// Models
|
||||||
|
m_proxyModel->setSourceModel(m_model);
|
||||||
|
m_proxyModel->setFilterKeyColumn(-1);
|
||||||
|
m_proxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
||||||
|
ui->stashView->setModel(m_proxyModel);
|
||||||
|
ui->stashView->setSelectionMode(QAbstractItemView::MultiSelection);
|
||||||
|
connect(ui->filterLineEdit, SIGNAL(filterChanged(QString)), m_proxyModel, SLOT(setFilterFixedString(QString)));
|
||||||
|
connect(ui->stashView->selectionModel(), SIGNAL(currentRowChanged(QModelIndex,QModelIndex)),
|
||||||
|
this, SLOT(enableButtons()));
|
||||||
|
connect(ui->stashView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||||
|
this, SLOT(enableButtons()));
|
||||||
|
connect(ui->stashView, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(showCurrent()));
|
||||||
|
ui->stashView->setFocus();
|
||||||
|
}
|
||||||
|
|
||||||
|
StashDialog::~StashDialog()
|
||||||
|
{
|
||||||
|
delete ui;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::changeEvent(QEvent *e)
|
||||||
|
{
|
||||||
|
QDialog::changeEvent(e);
|
||||||
|
switch (e->type()) {
|
||||||
|
case QEvent::LanguageChange:
|
||||||
|
ui->retranslateUi(this);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::refresh(const QString &repository, bool force)
|
||||||
|
{
|
||||||
|
if (m_repository == repository && !force)
|
||||||
|
return;
|
||||||
|
// Refresh
|
||||||
|
m_repository = repository;
|
||||||
|
if (m_repository.isEmpty()) {
|
||||||
|
ui->repositoryLabel->setText(tr("<No repository>"));
|
||||||
|
m_model->setStashes(QList<Stash>());
|
||||||
|
} else {
|
||||||
|
ui->repositoryLabel->setText(tr("Repository: %1").arg(repository));
|
||||||
|
QList<Stash> stashes;
|
||||||
|
gitClient()->synchronousStashList(m_repository, &stashes);
|
||||||
|
m_model->setStashes(stashes);
|
||||||
|
if (!stashes.isEmpty()) {
|
||||||
|
for(int c = 0; c < ColumnCount; c++)
|
||||||
|
ui->stashView->resizeColumnToContents(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
enableButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::deleteAll()
|
||||||
|
{
|
||||||
|
const QString title = tr("Delete stashes");
|
||||||
|
if (!ask(title, tr("Do you want to delete all stashes?")))
|
||||||
|
return;
|
||||||
|
QString errorMessage;
|
||||||
|
if (gitClient()->synchronousStashRemove(m_repository, QString(), &errorMessage)) {
|
||||||
|
refresh(m_repository, true);
|
||||||
|
} else {
|
||||||
|
warning(title, errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::deleteSelection()
|
||||||
|
{
|
||||||
|
const QList<int> rows = selectedRows();
|
||||||
|
QTC_ASSERT(!rows.isEmpty(), return)
|
||||||
|
const QString title = tr("Delete stashes");
|
||||||
|
if (!ask(title, tr("Do you want to delete %n stash(es)?", 0, rows.size())))
|
||||||
|
return;
|
||||||
|
QString errorMessage;
|
||||||
|
QStringList errors;
|
||||||
|
// Delete in reverse order as stashes rotate
|
||||||
|
for (int r = rows.size() - 1; r >= 0; r--)
|
||||||
|
if (!gitClient()->synchronousStashRemove(m_repository, m_model->at(r).name, &errorMessage))
|
||||||
|
errors.push_back(errorMessage);
|
||||||
|
refresh(m_repository, true);
|
||||||
|
if (!errors.isEmpty())
|
||||||
|
warning(title, errors.join(QString(QLatin1Char('\n'))));
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::showCurrent()
|
||||||
|
{
|
||||||
|
const int index = currentRow();
|
||||||
|
QTC_ASSERT(index >= 0, return)
|
||||||
|
gitClient()->show(m_repository, m_model->at(index).name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Suggest Branch name to restore 'stash@{0}' -> 'stash0-date'
|
||||||
|
static inline QString stashRestoreDefaultBranch(QString stash)
|
||||||
|
{
|
||||||
|
stash.remove(QLatin1Char('{'));
|
||||||
|
stash.remove(QLatin1Char('}'));
|
||||||
|
stash.remove(QLatin1Char('@'));
|
||||||
|
stash += QLatin1Char('-');
|
||||||
|
stash += QDateTime::currentDateTime().toString(QLatin1String("yyMMddhhmmss"));
|
||||||
|
return stash;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return next stash id 'stash@{0}' -> 'stash@{1}'
|
||||||
|
static inline QString nextStash(const QString &stash)
|
||||||
|
{
|
||||||
|
const int openingBracePos = stash.indexOf(QLatin1Char('{'));
|
||||||
|
if (openingBracePos == -1)
|
||||||
|
return QString();
|
||||||
|
const int closingBracePos = stash.indexOf(QLatin1Char('}'), openingBracePos + 2);
|
||||||
|
if (closingBracePos == -1)
|
||||||
|
return QString();
|
||||||
|
bool ok;
|
||||||
|
const int n = stash.mid(openingBracePos + 1, closingBracePos - openingBracePos - 1).toInt(&ok);
|
||||||
|
if (!ok)
|
||||||
|
return QString();
|
||||||
|
QString rc = stash.left(openingBracePos + 1);
|
||||||
|
rc += QString::number(n + 1);
|
||||||
|
rc += QLatin1Char('}');
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
StashDialog::ModifiedRepositoryAction StashDialog::promptModifiedRepository(const QString &stash)
|
||||||
|
{
|
||||||
|
QMessageBox box(QMessageBox::Question,
|
||||||
|
tr("Repository modified"),
|
||||||
|
tr("%1 cannot be restored since the repository is modified.\n"
|
||||||
|
"You can choose between stashing the changes or discarding them.").arg(stash),
|
||||||
|
QMessageBox::Cancel, this);
|
||||||
|
QPushButton *stashButton = box.addButton(tr("Stash"), QMessageBox::AcceptRole);
|
||||||
|
QPushButton *discardButton = box.addButton(tr("Discard"), QMessageBox::AcceptRole);
|
||||||
|
box.exec();
|
||||||
|
const QAbstractButton *clickedButton = box.clickedButton();
|
||||||
|
if (clickedButton == stashButton)
|
||||||
|
return ModifiedRepositoryStash;
|
||||||
|
if (clickedButton == discardButton)
|
||||||
|
return ModifiedRepositoryDiscard;
|
||||||
|
return ModifiedRepositoryCancel;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prompt for restore: Make sure repository is unmodified,
|
||||||
|
// prompt for a branch if desired or just ask to restore.
|
||||||
|
// Note that the stash to be restored changes if the user
|
||||||
|
// chooses to stash away modified repository.
|
||||||
|
bool StashDialog::promptForRestore(QString *stash,
|
||||||
|
QString *branch /* = 0*/,
|
||||||
|
QString *errorMessage)
|
||||||
|
{
|
||||||
|
const QString stashIn = *stash;
|
||||||
|
bool modifiedPromptShown = false;
|
||||||
|
switch (gitClient()->gitStatus(m_repository, false, 0, errorMessage)) {
|
||||||
|
case GitClient::StatusFailed:
|
||||||
|
return false;
|
||||||
|
case GitClient::StatusChanged: {
|
||||||
|
switch (promptModifiedRepository(*stash)) {
|
||||||
|
case ModifiedRepositoryCancel:
|
||||||
|
return false;
|
||||||
|
case ModifiedRepositoryStash:
|
||||||
|
if (gitClient()->synchronousStash(m_repository, QString(), GitClient::StashPromptDescription).isEmpty())
|
||||||
|
return false;
|
||||||
|
*stash = nextStash(*stash); // Our stash id to be restored changed
|
||||||
|
QTC_ASSERT(!stash->isEmpty(), return false)
|
||||||
|
break;
|
||||||
|
case ModifiedRepositoryDiscard:
|
||||||
|
if (!gitClient()->synchronousReset(m_repository))
|
||||||
|
return false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
modifiedPromptShown = true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case GitClient::StatusUnchanged:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// Prompt for branch or just ask.
|
||||||
|
if (branch) {
|
||||||
|
*branch = stashRestoreDefaultBranch(*stash);
|
||||||
|
if (!inputText(this, tr("Restore Stash to Branch"), tr("Branch:"), branch)
|
||||||
|
|| branch->isEmpty())
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
if (!modifiedPromptShown && !ask(tr("Stash Restore"), tr("Would you like to restore %1?").arg(stashIn)))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline QString msgRestoreFailedTitle(const QString &stash)
|
||||||
|
{
|
||||||
|
return StashDialog::tr("Error restoring %1").arg(stash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::restoreCurrent()
|
||||||
|
{
|
||||||
|
const int index = currentRow();
|
||||||
|
QTC_ASSERT(index >= 0, return)
|
||||||
|
QString errorMessage;
|
||||||
|
QString name = m_model->at(index).name;
|
||||||
|
// Make sure repository is not modified, restore. The command will
|
||||||
|
// output to window on success.
|
||||||
|
const bool success = promptForRestore(&name, 0, &errorMessage)
|
||||||
|
&& gitClient()->synchronousStashRestore(m_repository, name, QString(), &errorMessage);
|
||||||
|
if (success) {
|
||||||
|
refresh(m_repository, true); // Might have stashed away local changes.
|
||||||
|
} else {
|
||||||
|
if (!errorMessage.isEmpty())
|
||||||
|
warning(msgRestoreFailedTitle(name), errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::restoreCurrentInBranch()
|
||||||
|
{
|
||||||
|
const int index = currentRow();
|
||||||
|
QTC_ASSERT(index >= 0, return)
|
||||||
|
QString errorMessage;
|
||||||
|
QString branch;
|
||||||
|
QString name = m_model->at(index).name;
|
||||||
|
const bool success = promptForRestore(&name, &branch, &errorMessage)
|
||||||
|
&& gitClient()->synchronousStashRestore(m_repository, name, branch, &errorMessage);
|
||||||
|
if (success) {
|
||||||
|
refresh(m_repository, true); // git deletes the stash, unfortunately.
|
||||||
|
} else {
|
||||||
|
if (!errorMessage.isEmpty())
|
||||||
|
warning(msgRestoreFailedTitle(name), errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int StashDialog::currentRow() const
|
||||||
|
{
|
||||||
|
const QModelIndex proxyIndex = ui->stashView->currentIndex();
|
||||||
|
if (proxyIndex.isValid()) {
|
||||||
|
const QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
|
||||||
|
if (index.isValid())
|
||||||
|
return index.row();
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
QList<int> StashDialog::selectedRows() const
|
||||||
|
{
|
||||||
|
QList<int> rc;
|
||||||
|
foreach(const QModelIndex &proxyIndex, ui->stashView->selectionModel()->selectedRows()) {
|
||||||
|
const QModelIndex index = m_proxyModel->mapToSource(proxyIndex);
|
||||||
|
if (index.isValid())
|
||||||
|
rc.push_back(index.row());
|
||||||
|
}
|
||||||
|
qSort(rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::forceRefresh()
|
||||||
|
{
|
||||||
|
refresh(m_repository, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::enableButtons()
|
||||||
|
{
|
||||||
|
const bool hasStashes = m_model->rowCount();
|
||||||
|
const bool hasCurrentRow = hasStashes && currentRow() >= 0;
|
||||||
|
m_deleteAllButton->setEnabled(hasStashes);
|
||||||
|
m_showCurrentButton->setEnabled(hasCurrentRow);
|
||||||
|
m_restoreCurrentButton->setEnabled(hasCurrentRow);
|
||||||
|
m_restoreCurrentInBranchButton->setEnabled(hasCurrentRow);
|
||||||
|
const bool hasSelection = !ui->stashView->selectionModel()->selectedRows().isEmpty();
|
||||||
|
m_deleteSelectionButton->setEnabled(hasSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StashDialog::warning(const QString &title, const QString &what, const QString &details)
|
||||||
|
{
|
||||||
|
QMessageBox msgBox(QMessageBox::Warning, title, what, QMessageBox::Ok, this);
|
||||||
|
if (!details.isEmpty())
|
||||||
|
msgBox.setDetailedText(details);
|
||||||
|
msgBox.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool StashDialog::ask(const QString &title, const QString &what, bool defaultButton)
|
||||||
|
{
|
||||||
|
return QMessageBox::question(this, title, what, QMessageBox::Yes|QMessageBox::No,
|
||||||
|
defaultButton ? QMessageBox::Yes : QMessageBox::No) == QMessageBox::Yes;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Git
|
105
src/plugins/git/stashdialog.h
Normal file
105
src/plugins/git/stashdialog.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**************************************************************************
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator
|
||||||
|
**
|
||||||
|
** Copyright (c) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||||
|
**
|
||||||
|
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||||
|
**
|
||||||
|
** Commercial Usage
|
||||||
|
**
|
||||||
|
** Licensees holding valid Qt Commercial licenses may use this file in
|
||||||
|
** accordance with the Qt Commercial License Agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and Nokia.
|
||||||
|
**
|
||||||
|
** 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.
|
||||||
|
**
|
||||||
|
** If you are unsure which license is appropriate for your use, please
|
||||||
|
** contact the sales department at http://qt.nokia.com/contact.
|
||||||
|
**
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
#ifndef STASHDIALOG_H
|
||||||
|
#define STASHDIALOG_H
|
||||||
|
|
||||||
|
#include <QtGui/QDialog>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
class QPushButton;
|
||||||
|
class QModelIndex;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
|
namespace Git {
|
||||||
|
namespace Internal {
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class StashDialog;
|
||||||
|
}
|
||||||
|
class StashModel;
|
||||||
|
|
||||||
|
/* StashDialog: Non-modal dialog that manages the list of stashes
|
||||||
|
* of the repository. Offers to show, restore, restore to branch
|
||||||
|
* (in case restore fails due to conflicts) on current and
|
||||||
|
* delete on selection/all. */
|
||||||
|
|
||||||
|
class StashDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit StashDialog(QWidget *parent = 0);
|
||||||
|
~StashDialog();
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void refresh(const QString &repository, bool force);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void changeEvent(QEvent *e);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void deleteAll();
|
||||||
|
void deleteSelection();
|
||||||
|
void showCurrent();
|
||||||
|
void restoreCurrent();
|
||||||
|
void restoreCurrentInBranch();
|
||||||
|
void enableButtons();
|
||||||
|
void forceRefresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Prompt dialog for modified repositories. Ask to undo or stash away.
|
||||||
|
enum ModifiedRepositoryAction {
|
||||||
|
ModifiedRepositoryCancel,
|
||||||
|
ModifiedRepositoryStash,
|
||||||
|
ModifiedRepositoryDiscard
|
||||||
|
};
|
||||||
|
|
||||||
|
ModifiedRepositoryAction promptModifiedRepository(const QString &stash);
|
||||||
|
bool promptForRestore(QString *stash, QString *branch /* = 0 */, QString *errorMessage);
|
||||||
|
bool ask(const QString &title, const QString &what, bool defaultButton = true);
|
||||||
|
void warning(const QString &title, const QString &what, const QString &details = QString());
|
||||||
|
int currentRow() const;
|
||||||
|
QList<int> selectedRows() const; \
|
||||||
|
|
||||||
|
Ui::StashDialog *ui;
|
||||||
|
StashModel *m_model;
|
||||||
|
QSortFilterProxyModel *m_proxyModel;
|
||||||
|
QPushButton *m_deleteAllButton;
|
||||||
|
QPushButton *m_deleteSelectionButton;
|
||||||
|
QPushButton *m_showCurrentButton;
|
||||||
|
QPushButton *m_restoreCurrentButton;
|
||||||
|
QPushButton *m_restoreCurrentInBranchButton;
|
||||||
|
QPushButton *m_refreshButton;
|
||||||
|
QString m_repository;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Internal
|
||||||
|
} // namespace Git
|
||||||
|
|
||||||
|
#endif // STASHDIALOG_H
|
93
src/plugins/git/stashdialog.ui
Normal file
93
src/plugins/git/stashdialog.ui
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>Git::Internal::StashDialog</class>
|
||||||
|
<widget class="QDialog" name="Git::Internal::StashDialog">
|
||||||
|
<property name="geometry">
|
||||||
|
<rect>
|
||||||
|
<x>0</x>
|
||||||
|
<y>0</y>
|
||||||
|
<width>599</width>
|
||||||
|
<height>485</height>
|
||||||
|
</rect>
|
||||||
|
</property>
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Stashes</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<item>
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<item>
|
||||||
|
<widget class="QLabel" name="repositoryLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string notr="true">Repository: Dummy</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="Utils::FilterLineEdit" name="filterLineEdit"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QTreeView" name="stashView"/>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QDialogButtonBox" name="buttonBox">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="standardButtons">
|
||||||
|
<set>QDialogButtonBox::Close</set>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>Utils::FancyLineEdit</class>
|
||||||
|
<extends>QLineEdit</extends>
|
||||||
|
<header location="global">utils/fancylineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
<customwidget>
|
||||||
|
<class>Utils::FilterLineEdit</class>
|
||||||
|
<extends>Utils::FancyLineEdit</extends>
|
||||||
|
<header location="global">utils/filterlineedit.h</header>
|
||||||
|
</customwidget>
|
||||||
|
</customwidgets>
|
||||||
|
<resources/>
|
||||||
|
<connections>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>accepted()</signal>
|
||||||
|
<receiver>Git::Internal::StashDialog</receiver>
|
||||||
|
<slot>accept()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>248</x>
|
||||||
|
<y>254</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>157</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
<connection>
|
||||||
|
<sender>buttonBox</sender>
|
||||||
|
<signal>rejected()</signal>
|
||||||
|
<receiver>Git::Internal::StashDialog</receiver>
|
||||||
|
<slot>reject()</slot>
|
||||||
|
<hints>
|
||||||
|
<hint type="sourcelabel">
|
||||||
|
<x>316</x>
|
||||||
|
<y>260</y>
|
||||||
|
</hint>
|
||||||
|
<hint type="destinationlabel">
|
||||||
|
<x>286</x>
|
||||||
|
<y>274</y>
|
||||||
|
</hint>
|
||||||
|
</hints>
|
||||||
|
</connection>
|
||||||
|
</connections>
|
||||||
|
</ui>
|
@@ -68,6 +68,7 @@ bool MercurialControl::supportsOperation(Operation operation) const
|
|||||||
case Core::IVersionControl::CreateRepositoryOperation:
|
case Core::IVersionControl::CreateRepositoryOperation:
|
||||||
break;
|
break;
|
||||||
case Core::IVersionControl::OpenOperation:
|
case Core::IVersionControl::OpenOperation:
|
||||||
|
case Core::IVersionControl::SnapshotOperations:
|
||||||
supported = false;
|
supported = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -97,6 +98,26 @@ bool MercurialControl::vcsCreateRepository(const QString &directory)
|
|||||||
return mercurialClient->createRepositorySync(directory);
|
return mercurialClient->createRepositorySync(directory);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString MercurialControl::vcsCreateSnapshot(const QString &)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList MercurialControl::vcsSnapshots(const QString &)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MercurialControl::vcsRestoreSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool MercurialControl::vcsRemoveSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool MercurialControl::sccManaged(const QString &filename)
|
bool MercurialControl::sccManaged(const QString &filename)
|
||||||
{
|
{
|
||||||
const QFileInfo fi(filename);
|
const QFileInfo fi(filename);
|
||||||
|
@@ -57,6 +57,10 @@ public:
|
|||||||
bool vcsAdd(const QString &filename);
|
bool vcsAdd(const QString &filename);
|
||||||
bool vcsDelete(const QString &filename);
|
bool vcsDelete(const QString &filename);
|
||||||
bool vcsCreateRepository(const QString &directory);
|
bool vcsCreateRepository(const QString &directory);
|
||||||
|
QString vcsCreateSnapshot(const QString &topLevel);
|
||||||
|
QStringList vcsSnapshots(const QString &topLevel);
|
||||||
|
bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
|
||||||
bool sccManaged(const QString &filename);
|
bool sccManaged(const QString &filename);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
@@ -56,6 +56,7 @@ bool PerforceVersionControl::supportsOperation(Operation operation) const
|
|||||||
case OpenOperation:
|
case OpenOperation:
|
||||||
return true;
|
return true;
|
||||||
case CreateRepositoryOperation:
|
case CreateRepositoryOperation:
|
||||||
|
case SnapshotOperations:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@@ -84,6 +85,26 @@ bool PerforceVersionControl::vcsCreateRepository(const QString &)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString PerforceVersionControl::vcsCreateSnapshot(const QString &)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList PerforceVersionControl::vcsSnapshots(const QString &)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerforceVersionControl::vcsRestoreSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PerforceVersionControl::vcsRemoveSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool PerforceVersionControl::managesDirectory(const QString &directory) const
|
bool PerforceVersionControl::managesDirectory(const QString &directory) const
|
||||||
{
|
{
|
||||||
const bool rc = m_plugin->managesDirectory(directory);
|
const bool rc = m_plugin->managesDirectory(directory);
|
||||||
|
@@ -53,6 +53,10 @@ public:
|
|||||||
virtual bool vcsAdd(const QString &fileName);
|
virtual bool vcsAdd(const QString &fileName);
|
||||||
virtual bool vcsDelete(const QString &filename);
|
virtual bool vcsDelete(const QString &filename);
|
||||||
virtual bool vcsCreateRepository(const QString &directory);
|
virtual bool vcsCreateRepository(const QString &directory);
|
||||||
|
virtual QString vcsCreateSnapshot(const QString &topLevel);
|
||||||
|
virtual QStringList vcsSnapshots(const QString &topLevel);
|
||||||
|
virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
|
||||||
void emitRepositoryChanged(const QString &s);
|
void emitRepositoryChanged(const QString &s);
|
||||||
void emitFilesChanged(const QStringList &l);
|
void emitFilesChanged(const QStringList &l);
|
||||||
|
@@ -55,6 +55,7 @@ bool SubversionControl::supportsOperation(Operation operation) const
|
|||||||
break;
|
break;
|
||||||
case OpenOperation:
|
case OpenOperation:
|
||||||
case CreateRepositoryOperation:
|
case CreateRepositoryOperation:
|
||||||
|
case SnapshotOperations:
|
||||||
rc = false;
|
rc = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -84,6 +85,26 @@ bool SubversionControl::vcsCreateRepository(const QString &)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QString SubversionControl::vcsCreateSnapshot(const QString &)
|
||||||
|
{
|
||||||
|
return QString();
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList SubversionControl::vcsSnapshots(const QString &)
|
||||||
|
{
|
||||||
|
return QStringList();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubversionControl::vcsRestoreSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SubversionControl::vcsRemoveSnapshot(const QString &, const QString &)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool SubversionControl::managesDirectory(const QString &directory) const
|
bool SubversionControl::managesDirectory(const QString &directory) const
|
||||||
{
|
{
|
||||||
return m_plugin->managesDirectory(directory);
|
return m_plugin->managesDirectory(directory);
|
||||||
|
@@ -54,6 +54,11 @@ public:
|
|||||||
virtual bool vcsDelete(const QString &filename);
|
virtual bool vcsDelete(const QString &filename);
|
||||||
virtual bool vcsCreateRepository(const QString &directory);
|
virtual bool vcsCreateRepository(const QString &directory);
|
||||||
|
|
||||||
|
virtual QString vcsCreateSnapshot(const QString &topLevel);
|
||||||
|
virtual QStringList vcsSnapshots(const QString &topLevel);
|
||||||
|
virtual bool vcsRestoreSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
virtual bool vcsRemoveSnapshot(const QString &topLevel, const QString &name);
|
||||||
|
|
||||||
void emitRepositoryChanged(const QString &);
|
void emitRepositoryChanged(const QString &);
|
||||||
void emitFilesChanged(const QStringList &);
|
void emitFilesChanged(const QStringList &);
|
||||||
|
|
||||||
|
@@ -30,6 +30,7 @@
|
|||||||
#include "vcsbaseplugin.h"
|
#include "vcsbaseplugin.h"
|
||||||
#include "vcsbasesubmiteditor.h"
|
#include "vcsbasesubmiteditor.h"
|
||||||
#include "vcsplugin.h"
|
#include "vcsplugin.h"
|
||||||
|
#include "vcsbaseoutputwindow.h"
|
||||||
#include "corelistener.h"
|
#include "corelistener.h"
|
||||||
|
|
||||||
#include <coreplugin/icore.h>
|
#include <coreplugin/icore.h>
|
||||||
@@ -353,6 +354,11 @@ struct VCSBasePluginPrivate {
|
|||||||
Core::IVersionControl *m_versionControl;
|
Core::IVersionControl *m_versionControl;
|
||||||
VCSBasePluginState m_state;
|
VCSBasePluginState m_state;
|
||||||
int m_actionState;
|
int m_actionState;
|
||||||
|
QAction *m_testSnapshotAction;
|
||||||
|
QAction *m_testListSnapshotsAction;
|
||||||
|
QAction *m_testRestoreSnapshotAction;
|
||||||
|
QAction *m_testRemoveSnapshotAction;
|
||||||
|
QString m_testLastSnapshot;
|
||||||
|
|
||||||
static Internal::StateListener *m_listener;
|
static Internal::StateListener *m_listener;
|
||||||
};
|
};
|
||||||
@@ -360,7 +366,11 @@ struct VCSBasePluginPrivate {
|
|||||||
VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorId) :
|
VCSBasePluginPrivate::VCSBasePluginPrivate(const QString &submitEditorId) :
|
||||||
m_submitEditorId(submitEditorId),
|
m_submitEditorId(submitEditorId),
|
||||||
m_versionControl(0),
|
m_versionControl(0),
|
||||||
m_actionState(-1)
|
m_actionState(-1),
|
||||||
|
m_testSnapshotAction(0),
|
||||||
|
m_testListSnapshotsAction(0),
|
||||||
|
m_testRestoreSnapshotAction(0),
|
||||||
|
m_testRemoveSnapshotAction(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,6 +517,62 @@ void VCSBasePlugin::createRepository()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For internal tests: Create actions driving IVersionControl's snapshot interface.
|
||||||
|
QList<QAction*> VCSBasePlugin::createSnapShotTestActions()
|
||||||
|
{
|
||||||
|
if (!d->m_testSnapshotAction) {
|
||||||
|
d->m_testSnapshotAction = new QAction(QLatin1String("Take snapshot"), this);
|
||||||
|
connect(d->m_testSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestSnapshot()));
|
||||||
|
d->m_testListSnapshotsAction = new QAction(QLatin1String("List snapshots"), this);
|
||||||
|
connect(d->m_testListSnapshotsAction, SIGNAL(triggered()), this, SLOT(slotTestListSnapshots()));
|
||||||
|
d->m_testRestoreSnapshotAction = new QAction(QLatin1String("Restore snapshot"), this);
|
||||||
|
connect(d->m_testRestoreSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRestoreSnapshot()));
|
||||||
|
d->m_testRemoveSnapshotAction = new QAction(QLatin1String("Remove snapshot"), this);
|
||||||
|
connect(d->m_testRemoveSnapshotAction, SIGNAL(triggered()), this, SLOT(slotTestRemoveSnapshot()));
|
||||||
|
}
|
||||||
|
QList<QAction*> rc;
|
||||||
|
rc << d->m_testSnapshotAction << d->m_testListSnapshotsAction << d->m_testRestoreSnapshotAction
|
||||||
|
<< d->m_testRemoveSnapshotAction;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VCSBasePlugin::slotTestSnapshot()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(currentState().hasTopLevel(), return)
|
||||||
|
d->m_testLastSnapshot = versionControl()->vcsCreateSnapshot(currentState().topLevel());
|
||||||
|
qDebug() << "Snapshot " << d->m_testLastSnapshot;
|
||||||
|
VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshot: ") + d->m_testLastSnapshot);
|
||||||
|
if (!d->m_testLastSnapshot.isEmpty())
|
||||||
|
d->m_testRestoreSnapshotAction->setText(QLatin1String("Restore snapshot ") + d->m_testLastSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VCSBasePlugin::slotTestListSnapshots()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(currentState().hasTopLevel(), return)
|
||||||
|
const QStringList snapshots = versionControl()->vcsSnapshots(currentState().topLevel());
|
||||||
|
qDebug() << "Snapshots " << snapshots;
|
||||||
|
VCSBaseOutputWindow::instance()->append(QLatin1String("Snapshots: ") + snapshots.join(QLatin1String(", ")));
|
||||||
|
}
|
||||||
|
|
||||||
|
void VCSBasePlugin::slotTestRestoreSnapshot()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
|
||||||
|
const bool ok = versionControl()->vcsRestoreSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
|
||||||
|
const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" restored") : QLatin1String(" failed"));
|
||||||
|
qDebug() << msg;
|
||||||
|
VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VCSBasePlugin::slotTestRemoveSnapshot()
|
||||||
|
{
|
||||||
|
QTC_ASSERT(currentState().hasTopLevel() && !d->m_testLastSnapshot.isEmpty(), return)
|
||||||
|
const bool ok = versionControl()->vcsRemoveSnapshot(currentState().topLevel(), d->m_testLastSnapshot);
|
||||||
|
const QString msg = d->m_testLastSnapshot+ (ok ? QLatin1String(" removed") : QLatin1String(" failed"));
|
||||||
|
qDebug() << msg;
|
||||||
|
VCSBaseOutputWindow::instance()->append(msg);
|
||||||
|
d->m_testLastSnapshot.clear();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace VCSBase
|
} // namespace VCSBase
|
||||||
|
|
||||||
#include "vcsbaseplugin.moc"
|
#include "vcsbaseplugin.moc"
|
||||||
|
@@ -34,7 +34,8 @@
|
|||||||
|
|
||||||
#include <extensionsystem/iplugin.h>
|
#include <extensionsystem/iplugin.h>
|
||||||
|
|
||||||
#include <QSharedDataPointer>
|
#include <QtCore/QSharedDataPointer>
|
||||||
|
#include <QtCore/QList>
|
||||||
|
|
||||||
QT_BEGIN_NAMESPACE
|
QT_BEGIN_NAMESPACE
|
||||||
class QAction;
|
class QAction;
|
||||||
@@ -149,6 +150,9 @@ public:
|
|||||||
const VCSBasePluginState ¤tState() const;
|
const VCSBasePluginState ¤tState() const;
|
||||||
Core::IVersionControl *versionControl() const;
|
Core::IVersionControl *versionControl() const;
|
||||||
|
|
||||||
|
// For internal tests: Create actions driving IVersionControl's snapshot interface.
|
||||||
|
QList<QAction*> createSnapShotTestActions();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
// Convenience slot for "Delete current file" action. Prompts to
|
// Convenience slot for "Delete current file" action. Prompts to
|
||||||
// delete the file via VCSManager.
|
// delete the file via VCSManager.
|
||||||
@@ -175,6 +179,10 @@ protected:
|
|||||||
private slots:
|
private slots:
|
||||||
void slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result);
|
void slotSubmitEditorAboutToClose(VCSBaseSubmitEditor *submitEditor, bool *result);
|
||||||
void slotStateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
|
void slotStateChanged(const VCSBase::Internal::State &s, Core::IVersionControl *vc);
|
||||||
|
void slotTestSnapshot();
|
||||||
|
void slotTestListSnapshots();
|
||||||
|
void slotTestRestoreSnapshot();
|
||||||
|
void slotTestRemoveSnapshot();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
VCSBasePluginPrivate *d;
|
VCSBasePluginPrivate *d;
|
||||||
|
@@ -222,26 +222,9 @@ QString QScriptIndenter::trimmedCodeLine(const QString &t)
|
|||||||
trimmed.append(QLatin1Char('X'));
|
trimmed.append(QLatin1Char('X'));
|
||||||
|
|
||||||
} else if (token.is(QScriptIncrementalScanner::Token::Comment)) {
|
} else if (token.is(QScriptIncrementalScanner::Token::Comment)) {
|
||||||
int i = 0;
|
for (int i = 0; i < token.length; ++i)
|
||||||
int e = token.length;
|
|
||||||
|
|
||||||
if (token.offset > 0 || startState == 0) {
|
|
||||||
trimmed.append(QLatin1String("/*"));
|
|
||||||
i += 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool needEndOfComment = false;
|
|
||||||
if (e > 2 && token.end() == t.length() && scanner.endState() != 0) {
|
|
||||||
needEndOfComment = true;
|
|
||||||
e -= 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (; i < e; ++i)
|
|
||||||
trimmed.append(QLatin1Char(' '));
|
trimmed.append(QLatin1Char(' '));
|
||||||
|
|
||||||
if (needEndOfComment)
|
|
||||||
trimmed.append(QLatin1String("*/"));
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
trimmed.append(t.midRef(token.offset, token.length));
|
trimmed.append(t.midRef(token.offset, token.length));
|
||||||
}
|
}
|
||||||
@@ -355,39 +338,6 @@ bool QScriptIndenter::readLine()
|
|||||||
|
|
||||||
yyLinizerState.line = trimmedCodeLine(yyLinizerState.line);
|
yyLinizerState.line = trimmedCodeLine(yyLinizerState.line);
|
||||||
|
|
||||||
/*
|
|
||||||
Remove C-style comments that span multiple lines. If the
|
|
||||||
bottom line starts in a C-style comment, we are not aware
|
|
||||||
of that and eventually yyLine will contain a slash-aster.
|
|
||||||
|
|
||||||
Notice that both if's can be executed, since
|
|
||||||
yyLinizerState.inCComment is potentially set to false in
|
|
||||||
the first if. The order of the if's is also important.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (yyLinizerState.inComment) {
|
|
||||||
const QLatin1String slashAster("/*");
|
|
||||||
|
|
||||||
k = yyLinizerState.line.indexOf(slashAster);
|
|
||||||
if (k == -1) {
|
|
||||||
yyLinizerState.line.clear();
|
|
||||||
} else {
|
|
||||||
yyLinizerState.line.truncate(k);
|
|
||||||
yyLinizerState.inComment = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!yyLinizerState.inComment) {
|
|
||||||
const QLatin1String asterSlash("*/");
|
|
||||||
|
|
||||||
k = yyLinizerState.line.indexOf(asterSlash);
|
|
||||||
if (k != -1) {
|
|
||||||
for (int i = 0; i < k + 2; i++)
|
|
||||||
eraseChar(yyLinizerState.line, i, QLatin1Char(' '));
|
|
||||||
yyLinizerState.inComment = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Remove preprocessor directives.
|
Remove preprocessor directives.
|
||||||
*/
|
*/
|
||||||
@@ -447,7 +397,6 @@ bool QScriptIndenter::readLine()
|
|||||||
void QScriptIndenter::startLinizer()
|
void QScriptIndenter::startLinizer()
|
||||||
{
|
{
|
||||||
yyLinizerState.braceDepth = 0;
|
yyLinizerState.braceDepth = 0;
|
||||||
yyLinizerState.inComment = false;
|
|
||||||
yyLinizerState.pendingRightBrace = false;
|
yyLinizerState.pendingRightBrace = false;
|
||||||
|
|
||||||
yyLine = &yyLinizerState.line;
|
yyLine = &yyLinizerState.line;
|
||||||
@@ -497,30 +446,7 @@ int QScriptIndenter::indentWhenBottomLineStartsInMultiLineComment()
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString codeLine = trimmedCodeLine(blockText);
|
return indentOfLine(blockText);
|
||||||
|
|
||||||
int k = codeLine.lastIndexOf(QLatin1String("/*"));
|
|
||||||
if (k == -1) {
|
|
||||||
/*
|
|
||||||
We found a normal text line in a comment. Align the
|
|
||||||
bottom line with the text on this line.
|
|
||||||
*/
|
|
||||||
return indentOfLine(codeLine);
|
|
||||||
} else {
|
|
||||||
/*
|
|
||||||
The C-style comment starts on this line. If there is
|
|
||||||
text on the same line, align with it. Otherwise, align
|
|
||||||
with the slash-aster plus a given offset.
|
|
||||||
*/
|
|
||||||
int indent = columnForIndex(codeLine, k);
|
|
||||||
k += 2;
|
|
||||||
while (k < yyLine->length()) {
|
|
||||||
if (!codeLine.at(k).isSpace())
|
|
||||||
return columnForIndex(codeLine, k);
|
|
||||||
k++;
|
|
||||||
}
|
|
||||||
return indent + ppCommentOffset;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@@ -94,13 +94,11 @@ private:
|
|||||||
LinizerState()
|
LinizerState()
|
||||||
: braceDepth(0),
|
: braceDepth(0),
|
||||||
leftBraceFollows(false),
|
leftBraceFollows(false),
|
||||||
inComment(false),
|
|
||||||
pendingRightBrace(false)
|
pendingRightBrace(false)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
int braceDepth;
|
int braceDepth;
|
||||||
bool leftBraceFollows;
|
bool leftBraceFollows;
|
||||||
bool inComment;
|
|
||||||
bool pendingRightBrace;
|
bool pendingRightBrace;
|
||||||
QString line;
|
QString line;
|
||||||
QList<QScriptIncrementalScanner::Token> tokens;
|
QList<QScriptIncrementalScanner::Token> tokens;
|
||||||
|
Reference in New Issue
Block a user