Commit a99fc39a authored by Aleix Pol Gonzalez's avatar Aleix Pol Gonzalez 🐧
Browse files

Added a stash management facility for git support.

Solved some minor issues.
parent 148a8a5f
......@@ -21,14 +21,15 @@ add_subdirectory(tests)
add_subdirectory(icons)
set(kdevgit_PART_SRCS
gitclonejob.cpp
stashmanagerdialog.cpp
gitclonejob.cpp
gitplugin.cpp
)
kde4_add_ui_files(kdevgit_PART_SRCS stashmanagerdialog.ui)
kde4_add_plugin(kdevgit ${kdevgit_PART_SRCS})
target_link_libraries(kdevgit
${KDE4_KDEUI_LIBS}
${KDEVPLATFORM_UTIL_LIBRARIES}
......
......@@ -43,6 +43,10 @@
#include <vcs/widgets/standardvcslocationwidget.h>
#include <KIO/CopyJob>
#include "gitclonejob.h"
#include <interfaces/contextmenuextension.h>
#include <QMenu>
#include <interfaces/iruncontroller.h>
#include "stashmanagerdialog.h"
K_PLUGIN_FACTORY(KDevGitFactory, registerPlugin<GitPlugin>(); )
K_EXPORT_PLUGIN(KDevGitFactory(KAboutData("kdevgit","kdevgit",ki18n("Git"),"0.1",ki18n("A plugin to support git version control systems"), KAboutData::License_GPL)))
......@@ -150,6 +154,58 @@ GitPlugin::GitPlugin( QObject *parent, const QVariantList & )
GitPlugin::~GitPlugin()
{}
bool emptyOutput(DVcsJob* job)
{
QScopedPointer<DVcsJob> _job(job);
if(job->exec() && job->status()==VcsJob::JobSucceeded)
return job->rawOutput().trimmed().isEmpty();
return false;
}
bool GitPlugin::hasStashes(const QDir& repository)
{
return !emptyOutput(gitStash(repository, QStringList("list"), KDevelop::OutputJob::Silent));
}
bool GitPlugin::hasModifications(const KUrl& file)
{
return !emptyOutput(lsFiles(dotGitDirectory(file), QStringList("-m"), OutputJob::Silent));
}
void GitPlugin::additionalMenuEntries(QMenu* menu, const KUrl::List& urls)
{
m_urls = urls;
QDir dir=urlDir(urls);
bool modif = hasModifications(urls.first());
bool canApply = !modif && hasStashes(dir);
menu->addSeparator()->setText(i18n("Git Stashes"));
menu->addAction(i18n("Stash Manager"), this, SLOT(ctxStashManager()))->setEnabled(canApply);
menu->addAction(i18n("Push Stash"), this, SLOT(ctxPushStash()))->setEnabled(modif);
menu->addAction(i18n("Pop Stash"), this, SLOT(ctxPopStash()))->setEnabled(canApply);
}
void GitPlugin::ctxPushStash()
{
VcsJob* job = gitStash(urlDir(m_urls), QStringList(), KDevelop::OutputJob::Verbose);
ICore::self()->runController()->registerJob(job);
}
void GitPlugin::ctxPopStash()
{
VcsJob* job = gitStash(urlDir(m_urls), QStringList("pop"), KDevelop::OutputJob::Verbose);
ICore::self()->runController()->registerJob(job);
}
void GitPlugin::ctxStashManager()
{
QPointer<StashManagerDialog> d = new StashManagerDialog(urlDir(m_urls), this, 0);
d->exec();
delete d;
}
DVcsJob* GitPlugin::errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity=OutputJob::Verbose)
{
DVcsJob* j = new DVcsJob(QDir::temp(), this, verbosity);
......@@ -187,10 +243,9 @@ bool GitPlugin::isVersionControlled(const KUrl &path)
return isValidDirectory(path);
}
QString workDir = fsObject.path();
QString filename = fsObject.fileName();
QStringList otherFiles = getLsFiles(workDir, QStringList("--") << filename, KDevelop::OutputJob::Silent);
QStringList otherFiles = getLsFiles(fsObject.dir(), QStringList("--") << filename, KDevelop::OutputJob::Silent);
return !otherFiles.empty();
}
......@@ -237,8 +292,12 @@ VcsJob* GitPlugin::diff(const KUrl& fileOrDirectory, const KDevelop::VcsRevision
//TODO: control different types
DVcsJob* job = new DVcsJob(urlDir(fileOrDirectory), this);
*job << "git" << "diff" << "--no-prefix" << revisionInterval(srcRevision, dstRevision) << "--";
*job << (recursion == IBasicVersionControl::Recursive ? fileOrDirectory : preventRecursion(fileOrDirectory));
*job << "git" << "diff" << "--no-prefix";
QString revstr = revisionInterval(srcRevision, dstRevision);
if(!revstr.isEmpty())
*job << revstr;
*job << "--" << (recursion == IBasicVersionControl::Recursive ? fileOrDirectory : preventRecursion(fileOrDirectory));
connect(job, SIGNAL(readyForParsing(KDevelop::DVcsJob*)), SLOT(parseGitDiffOutput(KDevelop::DVcsJob*)));
return job;
......@@ -416,23 +475,32 @@ VcsJob* GitPlugin::reset(const KUrl& repository, const QStringList &args, const
return job;
}
DVcsJob* GitPlugin::lsFiles(const QString &repository, const QStringList &args,
DVcsJob* GitPlugin::lsFiles(const QDir &repository, const QStringList &args,
OutputJob::OutputJobVerbosity verbosity)
{
DVcsJob* job = new DVcsJob(QDir(repository), this, verbosity);
DVcsJob* job = new DVcsJob(repository, this, verbosity);
*job << "git" << "ls-files" << args;
return job;
}
DVcsJob* GitPlugin::gitStash(const QDir& repository, const QStringList& args, OutputJob::OutputJobVerbosity verbosity)
{
DVcsJob* job = new DVcsJob(repository, this, verbosity);
*job << "git" << "stash" << args;
return job;
}
QString GitPlugin::curBranch(const QString &repository)
{
kDebug() << "Getting branch list";
QScopedPointer<DVcsJob> job(new DVcsJob(QDir(repository), this, OutputJob::Silent));
*job << "git" << "status" << "-b" << "--short";
*job << "git" << "symbolic-ref" << "HEAD";
if (job->exec() && job->status() == KDevelop::VcsJob::JobSucceeded) {
QString out = job->output();
return out.mid(3, out.indexOf('\n')-3);
int slashPos = out.lastIndexOf('/')+1;
kDebug() << "Getting branch list" << out.mid(slashPos, out.size()-slashPos).trimmed();
return out.mid(slashPos, out.size()-slashPos).trimmed();
}
return QString();
}
......@@ -823,7 +891,7 @@ void GitPlugin::parseGitStatusOutput(DVcsJob* job)
paths += *it;
//here we add the already up to date files
QStringList files = getLsFiles(job->directory().path(), QStringList() << "-c" << "--" << paths, OutputJob::Silent);
QStringList files = getLsFiles(job->directory(), QStringList() << "-c" << "--" << paths, OutputJob::Silent);
foreach(const QString& file, files) {
KUrl fileUrl = workingDir;
fileUrl.addPath(file);
......@@ -839,7 +907,7 @@ void GitPlugin::parseGitStatusOutput(DVcsJob* job)
job->setResults(statuses);
}
QStringList GitPlugin::getLsFiles(const QString &directory, const QStringList &args,
QStringList GitPlugin::getLsFiles(const QDir &directory, const QStringList &args,
KDevelop::OutputJob::OutputJobVerbosity verbosity)
{
QScopedPointer<DVcsJob> job(lsFiles(directory, args, verbosity));
......
......@@ -29,6 +29,7 @@
#include <outputview/outputjob.h>
#include <vcs/vcsjob.h>
class QDir;
namespace KDevelop
{
......@@ -138,13 +139,19 @@ public:
void parseLogOutput(const KDevelop::DVcsJob * job,
QList<DVcsEvent>& commits) const;
virtual void additionalMenuEntries(QMenu* menu, const KUrl::List& urls);
KDevelop::DVcsJob* gitStash(const QDir& repository, const QStringList& args, KDevelop::OutputJob::OutputJobVerbosity verbosity);
bool hasStashes(const QDir& repository);
bool hasModifications(const KUrl& repository);
protected:
KUrl repositoryRoot(const KUrl& path);
bool isValidDirectory(const KUrl &dirPath);
KDevelop::DVcsJob* lsFiles(const QString &repository,
KDevelop::DVcsJob* lsFiles(const QDir &repository,
const QStringList &args,
KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Verbose);
KDevelop::DVcsJob* gitRevList(const QString &repository,
......@@ -159,10 +166,14 @@ private slots:
void parseGitDiffOutput(KDevelop::DVcsJob* job);
void parseGitRepoLocationOutput(KDevelop::DVcsJob* job);
void parseGitStatusOutput(KDevelop::DVcsJob* job);
void ctxPushStash();
void ctxPopStash();
void ctxStashManager();
private:
//commit dialog "main" helper
QStringList getLsFiles(const QString &directory, const QStringList &args,
QStringList getLsFiles(const QDir &directory, const QStringList &args,
KDevelop::OutputJob::OutputJobVerbosity verbosity);
KDevelop::DVcsJob* errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity);
......@@ -171,6 +182,7 @@ private:
static KDevelop::VcsStatusInfo::State messageToState(const QString& ch);
QList<QStringList> branchesShas;
KUrl::List m_urls;
};
#endif
/*
* This file is part of KDevelop
* Copyright 2010 Aleix Pol Gonzalez <aleixpol@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "stashmanagerdialog.h"
#include "ui_stashmanagerdialog.h"
#include "gitplugin.h"
#include <interfaces/icore.h>
#include <interfaces/iruncontroller.h>
#include <KMessageBox>
#include <QInputDialog>
#include <vcs/dvcs/dvcsjob.h>
StashManagerDialog::StashManagerDialog(const QDir& stashed, GitPlugin* plugin, QWidget* parent)
: KDialog(parent), m_plugin(plugin), m_dir(stashed)
{
setButtons(KDialog::Close);
QWidget* w = new QWidget(this);
m_ui = new Ui::StashManager;
m_ui->setupUi(w);
StashModel* m = new StashModel(stashed, plugin, this);
m_ui->stashView->setModel(m);
connect(m_ui->apply, SIGNAL(clicked(bool)), SLOT(applyClicked()));
connect(m_ui->branch, SIGNAL(clicked(bool)), SLOT(branchClicked()));
connect(m_ui->pop, SIGNAL(clicked(bool)), SLOT(popClicked()));
connect(m_ui->drop, SIGNAL(clicked(bool)), SLOT(dropClicked()));
connect(m, SIGNAL(rowsInserted(QModelIndex,int,int)), SLOT(stashesFound()));
setMainWidget(w);
w->setEnabled(false); //we won't enable it until we have the model with data and selection
}
StashManagerDialog::~StashManagerDialog()
{
delete m_ui;
}
void StashManagerDialog::stashesFound()
{
QModelIndex firstIdx=m_ui->stashView->model()->index(0, 0);
m_ui->stashView->selectionModel()->select(firstIdx, QItemSelectionModel::ClearAndSelect);
mainWidget()->setEnabled(true);
}
QString StashManagerDialog::selection() const
{
QModelIndex idx = m_ui->stashView->currentIndex();
Q_ASSERT(idx.isValid());
return idx.data().toString();
}
void StashManagerDialog::runStash(const QStringList& arguments)
{
KDevelop::VcsJob* job = m_plugin->gitStash(m_dir, arguments, KDevelop::OutputJob::Verbose);
connect(job, SIGNAL(finished()), SLOT(accept()));
mainWidget()->setEnabled(false);
KDevelop::ICore::self()->runController()->registerJob(job);
}
void StashManagerDialog::applyClicked()
{
runStash(QStringList("apply") << selection());
}
void StashManagerDialog::popClicked()
{
runStash(QStringList("pop") << selection());
}
void StashManagerDialog::dropClicked()
{
QString sel = selection();
int ret = KMessageBox::questionYesNo(this, i18n("Are you sure you want to drop the stash '%1'?", sel));
if(ret == KMessageBox::Yes)
runStash(QStringList("drop") << sel);
}
void StashManagerDialog::branchClicked()
{
QString branchName = QInputDialog::getText(this, i18n("KDevelop - Git Stash"), i18n("Select a name for the new branch"));
if(!branchName.isEmpty())
runStash(QStringList("branch") << branchName << selection());
}
//////////////////StashModel
StashModel::StashModel(const QDir& dir, GitPlugin* git, QObject* parent)
: QStandardItemModel(parent)
{
KDevelop::VcsJob* job=git->gitStash(dir, QStringList("list"), KDevelop::OutputJob::Silent);
connect(job, SIGNAL(finished(KJob*)), SLOT(stashListReady(KJob*)));
KDevelop::ICore::self()->runController()->registerJob(job);
}
void StashModel::stashListReady(KJob* _job)
{
KDevelop::DVcsJob* job = qobject_cast<KDevelop::DVcsJob*>(_job);
QList< QByteArray > output = job->rawOutput().split('\n');
foreach(const QByteArray& line, output) {
QList< QByteArray > fields = line.split(':');
QList<QStandardItem*> elements;
foreach(const QByteArray& field, fields)
elements += new QStandardItem(QString(field.trimmed()));
appendRow(elements);
}
}
/*
* This file is part of KDevelop
* Copyright 2010 Aleix Pol Gonzalez <aleixpol@kde.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Library General Public License as
* published by the Free Software Foundation; either version 2 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef STASHMANAGERDIALOG_H
#define STASHMANAGERDIALOG_H
#include <KDialog>
#include <QStandardItemModel>
class KJob;
namespace Ui { class StashManager; }
class QDir;
class GitPlugin;
class StashManagerDialog : public KDialog
{
Q_OBJECT
public:
explicit StashManagerDialog(const QDir& stashed, GitPlugin* plugin, QWidget* parent);
virtual ~StashManagerDialog();
public slots:
void applyClicked();
void branchClicked();
void popClicked();
void dropClicked();
void stashesFound();
private:
QString selection() const;
void runStash(const QStringList& arguments);
Ui::StashManager* m_ui;
GitPlugin* m_plugin;
const QDir& m_dir;
};
class StashModel : public QStandardItemModel
{
Q_OBJECT
public:
explicit StashModel(const QDir& dir, GitPlugin* git, QObject* parent = 0);
private slots:
void stashListReady(KJob*);
};
#endif // STASHMANAGERDIALOG_H
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>StashManager</class>
<widget class="QWidget" name="StashManager">
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0" rowspan="5">
<widget class="QListView" name="stashView">
<property name="editTriggers">
<set>QAbstractItemView::NoEditTriggers</set>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QPushButton" name="apply">
<property name="whatsThis">
<string>Applies stash's patch</string>
</property>
<property name="text">
<string>Apply</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QPushButton" name="pop">
<property name="whatsThis">
<string>Applies stash's patch and drops the stash</string>
</property>
<property name="text">
<string>Pop</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QPushButton" name="branch">
<property name="whatsThis">
<string>Creates a new branch and applies the stash there, then it drops the stash.</string>
</property>
<property name="text">
<string>Branch</string>
</property>
</widget>
</item>
<item row="3" column="1">
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>77</height>
</size>
</property>
</spacer>
</item>
<item row="4" column="1">
<widget class="QPushButton" name="drop">
<property name="whatsThis">
<string>Removes the selected branch</string>
</property>
<property name="text">
<string>Drop</string>
</property>
</widget>
</item>
</layout>
</widget>
<resources/>
<connections/>
</ui>
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment