diff options
Diffstat (limited to 'src/plugins/git')
27 files changed, 3476 insertions, 0 deletions
diff --git a/src/plugins/git/ScmGit.pluginspec b/src/plugins/git/ScmGit.pluginspec new file mode 100644 index 0000000000..689cc30a57 --- /dev/null +++ b/src/plugins/git/ScmGit.pluginspec @@ -0,0 +1,13 @@ +<plugin name="ScmGit" version="0.1" compatVersion="0.1"> + <vendor>Nokia Corporation</vendor> + <copyright>(C) 2008 Nokia Corporation</copyright> + <license>Nokia Technology Preview License Agreement</license> + <description>Git integration.</description> + <url>http://www.trolltech.com/</url> + <dependencyList> + <dependency name="TextEditor" version="0.9.1"/> + <dependency name="ProjectExplorer" version="0.9.1"/> + <dependency name="Core" version="0.9.1"/> + <dependency name="VCSBase" version="0.9.1"/> + </dependencyList> +</plugin> diff --git a/src/plugins/git/TODO.txt b/src/plugins/git/TODO.txt new file mode 100644 index 0000000000..2913ba5f46 --- /dev/null +++ b/src/plugins/git/TODO.txt @@ -0,0 +1,26 @@ +- Make texts translateable +- Do not use QErrorMessage, Creator standard error instead? +Commands: + - P2: + - branch [list, create, delete] + - checkout [with/without creation] + - combine both above to a single dialog? + - P3: + - stash [creating, listing, applying] + - allow to use external viewer instead of greenhouse one + as these have more functionality usually + +GUI: + - Better diff view + - Commit view View (reuse diff view?) + - Commit action View + - Able to add further files to commit (list of modified/untracked files) + - use List for Log (and allow 10+ entries) + - Have commits clickable for 'git show' +Backend: + - Don't use forked processes, instead find a library connection like libgit-thin + - http://repo.or.cz/w/git/libgit-gsoc.git + - apply to SCM Manager in Greenhouse, currently it's mostly independent + +Suggestions: + - Bjorn: Use a "Summary" Lineedit in the commit dialog to make commits look nicer on gitweb or such. diff --git a/src/plugins/git/annotationhighlighter.cpp b/src/plugins/git/annotationhighlighter.cpp new file mode 100644 index 0000000000..534c45395b --- /dev/null +++ b/src/plugins/git/annotationhighlighter.cpp @@ -0,0 +1,53 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "annotationhighlighter.h" +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +GitAnnotationHighlighter::GitAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document) : + VCSBase::BaseAnnotationHighlighter(changeNumbers, document), + m_blank(QLatin1Char(' ')) +{ +} + +QString GitAnnotationHighlighter::changeNumber(const QString &block) const +{ + const int pos = block.indexOf(m_blank, 4); + return pos > 1 ? block.left(pos) : QString(); +} + +} +} diff --git a/src/plugins/git/annotationhighlighter.h b/src/plugins/git/annotationhighlighter.h new file mode 100644 index 0000000000..3368e4dc9f --- /dev/null +++ b/src/plugins/git/annotationhighlighter.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef ANNOTATIONHIGHLIGHTER_H +#define ANNOTATIONHIGHLIGHTER_H + +#include <vcsbase/baseannotationhighlighter.h> + +namespace Git { +namespace Internal { + +// Annotation highlighter for p4 triggering on 'changenumber:' +class GitAnnotationHighlighter : public VCSBase::BaseAnnotationHighlighter +{ + Q_OBJECT +public: + explicit GitAnnotationHighlighter(const ChangeNumbers &changeNumbers, + QTextDocument *document = 0); + +private: + virtual QString changeNumber(const QString &block) const; + + const QChar m_blank; +}; + +} //namespace Git +} //namespace Internal + +#endif diff --git a/src/plugins/git/changeselectiondialog.cpp b/src/plugins/git/changeselectiondialog.cpp new file mode 100644 index 0000000000..6491614b5f --- /dev/null +++ b/src/plugins/git/changeselectiondialog.cpp @@ -0,0 +1,71 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "changeselectiondialog.h" + +#include <QtGui/QFileDialog> +#include <QtGui/QMessageBox> + +using namespace Git::Internal; + +ChangeSelectionDialog::ChangeSelectionDialog(QWidget *parent) + : QDialog(parent) +{ + m_ui.setupUi(this); + connect(m_ui.repositoryButton, SIGNAL(clicked()), this, SLOT(selectWorkingDirectory())); +} + +void ChangeSelectionDialog::selectWorkingDirectory() +{ + static QString location = QString(); + location = QFileDialog::getExistingDirectory(this, + QLatin1String("Select Git repository"), + location); + if (location.isEmpty()) + return; + + // Verify that the location is a repository + // We are polite, we also allow to specify a directory, which is not + // the head directory of the repository. + QDir repository(location); + do { + if (repository.entryList(QDir::AllDirs).contains(QLatin1String(".git"))) { + m_ui.repositoryEdit->setText(repository.absolutePath()); + return; + } + } while (repository.cdUp()); + + // Did not find a repo + QMessageBox::critical(this, QLatin1String("Error"), + QLatin1String("Selected directory is not a Git repository")); + +} diff --git a/src/plugins/git/changeselectiondialog.h b/src/plugins/git/changeselectiondialog.h new file mode 100644 index 0000000000..02d0aa76d3 --- /dev/null +++ b/src/plugins/git/changeselectiondialog.h @@ -0,0 +1,63 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef CHANGESELECTIONDIALOG_H +#define CHANGESELECTIONDIALOG_H + +#include <QtGui/QDialog> + +#include "ui_changeselectiondialog.h" + +namespace Git { +namespace Internal { + + class GitPlugin; + +class ChangeSelectionDialog : public QDialog +{ + Q_OBJECT +public: + ChangeSelectionDialog(QWidget *parent = 0); + +public slots: + void selectWorkingDirectory(); + +private: + friend class GitPlugin; + Ui_ChangeSelectionDialog m_ui; + +}; + +} //namespace Internal +} //namespace Git + +#endif // CHANGESELECTIONDIALOG_H diff --git a/src/plugins/git/changeselectiondialog.ui b/src/plugins/git/changeselectiondialog.ui new file mode 100644 index 0000000000..34f2718ec8 --- /dev/null +++ b/src/plugins/git/changeselectiondialog.ui @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>ChangeSelectionDialog</class> + <widget class="QDialog" name="ChangeSelectionDialog"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>401</width> + <height>142</height> + </rect> + </property> + <property name="windowTitle"> + <string>Dialog</string> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Repository Location:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="repositoryEdit"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="repositoryButton"> + <property name="text"> + <string>Select</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Change:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="changeNumberEdit"/> + </item> + <item row="2" column="0" colspan="3"> + <widget class="QDialogButtonBox" name="buttonBox"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="standardButtons"> + <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set> + </property> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections> + <connection> + <sender>buttonBox</sender> + <signal>accepted()</signal> + <receiver>ChangeSelectionDialog</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>ChangeSelectionDialog</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> diff --git a/src/plugins/git/commitdata.cpp b/src/plugins/git/commitdata.cpp new file mode 100644 index 0000000000..aafafe1566 --- /dev/null +++ b/src/plugins/git/commitdata.cpp @@ -0,0 +1,96 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "commitdata.h" + +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +void GitSubmitEditorPanelInfo::clear() +{ + repository.clear(); + description.clear(); + branch.clear(); +} + +QDebug operator<<(QDebug d, const GitSubmitEditorPanelInfo &data) +{ + d.nospace() << "Rep: " << data.repository << " Descr: " << data.description + << " branch: " << data.branch; + return d; +} + +void GitSubmitEditorPanelData::clear() +{ + author.clear(); + email.clear(); +} + +QString GitSubmitEditorPanelData::authorString() const +{ + QString rc; + rc += QLatin1Char('"'); + rc += author; + rc += QLatin1String(" <"); + rc += email; + rc += QLatin1String(">\""); + return rc; +} + +QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &data) +{ + d.nospace() << " author:" << data.author << " email: " << data.email; + return d; +} + +void CommitData::clear() +{ + panelInfo.clear(); + panelData.clear(); + + commitFiles.clear(); + notUpdatedFiles.clear(); + untrackedFiles.clear(); +} + +QDebug operator<<(QDebug d, const CommitData &data) +{ + d << data.panelInfo << data.panelData; + d.nospace() << "Commit: " << data.commitFiles << " Not updated: " + << data.notUpdatedFiles << " Untracked: " << data.untrackedFiles; + return d; +} + +} +} diff --git a/src/plugins/git/commitdata.h b/src/plugins/git/commitdata.h new file mode 100644 index 0000000000..94a82005ae --- /dev/null +++ b/src/plugins/git/commitdata.h @@ -0,0 +1,81 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef COMMITDATA_H +#define COMMITDATA_H + +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QDebug; +QT_END_NAMESPACE + +namespace Git { +namespace Internal { + + // Read-only + struct GitSubmitEditorPanelInfo { + void clear(); + QString repository; + QString description; + QString branch; + }; + + QDebug operator<<(QDebug d, const GitSubmitEditorPanelInfo &); + + struct GitSubmitEditorPanelData { + void clear(); + // Format as "John Doe <jdoe@foobar.com>" + QString authorString() const; + + QString author; + QString email; + }; + + QDebug operator<<(QDebug d, const GitSubmitEditorPanelData &); + + struct CommitData { + void clear(); + GitSubmitEditorPanelInfo panelInfo; + GitSubmitEditorPanelData panelData; + QStringList commitFiles; + QStringList notUpdatedFiles; + QStringList untrackedFiles; + }; + + QDebug operator<<(QDebug d, const CommitData &); + + +} +} + +#endif diff --git a/src/plugins/git/git.pro b/src/plugins/git/git.pro new file mode 100644 index 0000000000..258639dcbe --- /dev/null +++ b/src/plugins/git/git.pro @@ -0,0 +1,35 @@ +TEMPLATE = lib +TARGET = ScmGit +include(../../qworkbenchplugin.pri) +include(../../plugins/projectexplorer/projectexplorer.pri) +include(../../plugins/texteditor/texteditor.pri) +include(../../plugins/coreplugin/coreplugin.pri) +include(../../plugins/vcsbase/vcsbase.pri) +include(../../libs/utils/utils.pri) + +HEADERS += gitplugin.h \ + gitconstants.h \ + gitoutputwindow.h \ + gitclient.h \ + changeselectiondialog.h \ + commitdata.h \ + settingspage.h \ + giteditor.h \ + annotationhighlighter.h \ + gitsubmiteditorwidget.h \ + gitsubmiteditor.h + +SOURCES += gitplugin.cpp \ + gitoutputwindow.cpp \ + gitclient.cpp \ + changeselectiondialog.cpp \ + commitdata.cpp \ + settingspage.cpp \ + giteditor.cpp \ + annotationhighlighter.cpp \ + gitsubmiteditorwidget.cpp \ + gitsubmiteditor.cpp + +FORMS += changeselectiondialog.ui \ + settingspage.ui \ + gitsubmitpanel.ui diff --git a/src/plugins/git/gitclient.cpp b/src/plugins/git/gitclient.cpp new file mode 100644 index 0000000000..b3f7107861 --- /dev/null +++ b/src/plugins/git/gitclient.cpp @@ -0,0 +1,635 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitclient.h" +#include "gitplugin.h" +#include "gitconstants.h" +#include "commitdata.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/progressmanager/progressmanagerinterface.h> +#include <vcsbase/vcsbaseeditor.h> +#include <texteditor/itexteditor.h> + +#include <QtCore/QRegExp> +#include <QtCore/QTemporaryFile> +#include <QtCore/QFuture> + +#include <QtGui/QErrorMessage> + +using namespace Git; +using namespace Git::Internal; + +const char* const kGitCommand = "git"; +const char* const kGitDirectoryC = ".git"; +const char* const kBranchIndicatorC = "# On branch"; + +static inline QString msgServerFailure() +{ + return GitClient::tr( +"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."); +} + +inline Core::IEditor* locateEditor(const Core::ICore *core, const char *property, const QString &entry) +{ + foreach (Core::IEditor *ed, core->editorManager()->openedEditors()) + if (ed->property(property).toString() == entry) + return ed; + return 0; +} + +GitClient::GitClient(GitPlugin* plugin, Core::ICore *core) : + m_msgWait(tr("Waiting for data...")), + m_plugin(plugin), + m_core(core) +{ +} + +GitClient::~GitClient() +{ +} + +bool GitClient::vcsOpen(const QString &fileName) +{ + return m_plugin->vcsOpen(fileName); +} + +QString GitClient::findRepositoryForFile(const QString &fileName) +{ + const QString gitDirectory = QLatin1String(kGitDirectoryC); + const QFileInfo info(fileName); + QDir dir = info.absoluteDir(); + do { + if (dir.entryList(QDir::AllDirs|QDir::Hidden).contains(gitDirectory)) + return dir.absolutePath(); + } while (dir.cdUp()); + + return QString(); +} + +QString GitClient::findRepositoryForDirectory(const QString &dir) +{ + const QString gitDirectory = QLatin1String(kGitDirectoryC); + QDir directory(dir); + do { + if (directory.entryList(QDir::AllDirs|QDir::Hidden).contains(gitDirectory)) + return directory.absolutePath(); + } while (directory.cdUp()); + + return QString(); +} + +// Return source file or directory string depending on parameters +// ('git diff XX' -> 'XX' , 'git diff XX file' -> 'XX/file'). +static QString source(const QString &workingDirectory, const QString &fileName) +{ + if (fileName.isEmpty()) + return workingDirectory; + QString rc = workingDirectory; + if (!rc.isEmpty() && !rc.endsWith(QDir::separator())) + rc += QDir::separator(); + rc += fileName; + return rc; +} + +/* Create an editor associated to VCS output of a source file/directory + * (using the file's codec). Makes use of a dynamic property to find an + * existing instance and to reuse it (in case, say, 'git diff foo' is + * already open). */ +VCSBase::VCSBaseEditor + *GitClient::createVCSEditor(const QString &kind, + QString title, + // Source file or directory + const QString &source, + bool setSourceCodec, + // Dynamic property and value to identify that editor + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const +{ + VCSBase::VCSBaseEditor *rc = 0; + Core::IEditor* outputEditor = locateEditor(m_core, registerDynamicProperty, dynamicPropertyValue); + if (outputEditor) { + // Exists already + outputEditor->createNew(m_msgWait); + rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor); + Q_ASSERT(rc); + m_core->editorManager()->setCurrentEditor(outputEditor); + } else { + // Create new, set wait message, set up with source and codec + outputEditor = m_core->editorManager()->newFile(kind, &title, m_msgWait); + outputEditor->setProperty(registerDynamicProperty, dynamicPropertyValue); + rc = VCSBase::VCSBaseEditor::getVcsBaseEditor(outputEditor); + Q_ASSERT(rc); + rc->setSource(source); + if (setSourceCodec) + rc->setCodec(VCSBase::VCSBaseEditor::getCodec(m_core, source)); + } + return rc; +} + +void GitClient::diff(const QString &workingDirectory, const QStringList &fileNames) +{ + if (Git::Constants::debug) + qDebug() << "diff" << workingDirectory << fileNames; + QStringList arguments; + arguments << QLatin1String("diff") << fileNames; + + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + const QString title = tr("Git Diff"); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, workingDirectory, true, "originalFileName", workingDirectory); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); + +} + +void GitClient::diff(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "diff" << workingDirectory << fileName; + QStringList arguments; + arguments << QLatin1String("diff"); + if (!fileName.isEmpty()) + arguments << fileName; + + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + const QString title = tr("Git Diff %1").arg(fileName); + const QString sourceFile = source(workingDirectory, fileName); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "originalFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::status(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("status")), m_plugin->m_outputWindow, 0,true); +} + +void GitClient::log(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "log" << workingDirectory << fileName; + QStringList arguments; + int logCount = 10; + if (m_plugin->m_settingsPage && m_plugin->m_settingsPage->logCount() > 0) + logCount = m_plugin->m_settingsPage->logCount(); + + arguments << QLatin1String("log") << QLatin1String("-n") + << QString::number(logCount); + if (!fileName.isEmpty()) + arguments << fileName; + + const QString title = tr("Git Log %1").arg(fileName); + const QString kind = QLatin1String(Git::Constants::GIT_LOG_EDITOR_KIND); + const QString sourceFile = source(workingDirectory, fileName); + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, false, "logFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::show(const QString &source, const QString &id) +{ + if (Git::Constants::debug) + qDebug() << "show" << source << id; + QStringList arguments(QLatin1String("show")); + arguments << id; + + const QString title = tr("Git Show %1").arg(id); + const QString kind = QLatin1String(Git::Constants::GIT_DIFF_EDITOR_KIND); + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, source, true, "show", id); + + const QFileInfo sourceFi(source); + const QString workDir = sourceFi.isDir() ? sourceFi.absoluteFilePath() : sourceFi.absolutePath(); + executeGit(workDir, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::blame(const QString &workingDirectory, const QString &fileName) +{ + if (Git::Constants::debug) + qDebug() << "blame" << workingDirectory << fileName; + QStringList arguments(QLatin1String("blame")); + arguments << fileName; + + const QString kind = QLatin1String(Git::Constants::GIT_BLAME_EDITOR_KIND); + const QString title = tr("Git Blame %1").arg(fileName); + const QString sourceFile = source(workingDirectory, fileName); + + VCSBase::VCSBaseEditor *editor = createVCSEditor(kind, title, sourceFile, true, "blameFileName", sourceFile); + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, editor); +} + +void GitClient::checkout(const QString &workingDirectory, const QString &fileName) +{ + // Passing an empty argument as the file name is very dangereous, since this makes + // git checkout apply to all files. Almost looks like a bug in git. + if (fileName.isEmpty()) + return; + + QStringList arguments; + arguments << QLatin1String("checkout") << QLatin1String("HEAD") << QLatin1String("--") + << fileName; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +void GitClient::hardReset(const QString &workingDirectory, const QString &commit) +{ + QStringList arguments; + arguments << QLatin1String("reset") << QLatin1String("--hard"); + if (!commit.isEmpty()) + arguments << commit; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +void GitClient::addFile(const QString &workingDirectory, const QString &fileName) +{ + QStringList arguments; + arguments << QLatin1String("add") << fileName; + + executeGit(workingDirectory, arguments, m_plugin->m_outputWindow, 0,true); +} + +bool GitClient::synchronousAdd(const QString &workingDirectory, const QStringList &files) +{ + QByteArray outputText; + QByteArray errorText; + QStringList arguments; + arguments << "add" << files; + const bool rc = synchronousGit(workingDirectory, arguments, &outputText, &errorText); + if (!rc) { + const QString errorMessage = tr("Unable to add %n file(s) to %1: %2", 0, files.size()). + arg(workingDirectory, QString::fromLocal8Bit(errorText)); + m_plugin->m_outputWindow->append(errorMessage); + m_plugin->m_outputWindow->popup(false); + } + return rc; +} + +void GitClient::executeGit(const QString &workingDirectory, const QStringList &arguments, + GitOutputWindow *outputWindow, VCSBase::VCSBaseEditor* editor, + bool outputToWindow) +{ + if (Git::Constants::debug) + qDebug() << "executeGit" << workingDirectory << arguments << editor; + outputWindow->clearContents(); + + QProcess process; + ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment(); + + if (m_plugin->m_settingsPage && !m_plugin->m_settingsPage->adoptEnvironment()) + environment.set(QLatin1String("PATH"), m_plugin->m_settingsPage->path()); + + GitCommand* command = new GitCommand(); + if (outputToWindow) { + Q_ASSERT(outputWindow); + connect(command, SIGNAL(outputText(QString)), outputWindow, SLOT(append(QString))); + connect(command, SIGNAL(outputData(QByteArray)), outputWindow, SLOT(appendData(QByteArray))); + } else { + Q_ASSERT(editor); + connect(command, SIGNAL(outputText(QString)), editor, SLOT(setPlainText(QString))); + connect(command, SIGNAL(outputData(QByteArray)), editor, SLOT(setPlainTextData(QByteArray))); + } + + if (outputWindow) + connect(command, SIGNAL(errorText(QString)), outputWindow, SLOT(append(QString))); + + command->execute(arguments, workingDirectory, environment); +} + +bool GitClient::synchronousGit(const QString &workingDirectory + , const QStringList &arguments + , QByteArray* outputText + , QByteArray* errorText) +{ + if (Git::Constants::debug) + qDebug() << "synchronousGit" << workingDirectory << arguments; + QProcess process; + + process.setWorkingDirectory(workingDirectory); + + ProjectExplorer::Environment environment = ProjectExplorer::Environment::systemEnvironment(); + if (m_plugin->m_settingsPage && !m_plugin->m_settingsPage->adoptEnvironment()) + environment.set(QLatin1String("PATH"), m_plugin->m_settingsPage->path()); + process.setEnvironment(environment.toStringList()); + + process.start(QLatin1String(kGitCommand), arguments); + if (!process.waitForFinished()) { + if (errorText) + *errorText = "Error: Git timed out"; + return false; + } + + if (outputText) + *outputText = process.readAllStandardOutput(); + + if (errorText) + *errorText = process.readAllStandardError(); + + if (Git::Constants::debug) + qDebug() << "synchronousGit ex=" << process.exitCode(); + return process.exitCode() == 0; +} + +/* Parse a git status file list: + * \code + # Changes to be committed: + #<tab>modified:<blanks>git.pro + # Changed but not updated: + #<tab>modified:<blanks>git.pro + # Untracked files: + #<tab>modified:<blanks>git.pro + \endcode +*/ +static bool parseFiles(const QStringList &lines, CommitData *d) +{ + enum State { None, CommitFiles, NotUpdatedFiles, UntrackedFiles }; + + const QString branchIndicator = QLatin1String(kBranchIndicatorC); + const QString commitIndicator = QLatin1String("# Changes to be committed:"); + const QString notUpdatedIndicator = QLatin1String("# Changed but not updated:"); + const QString untrackedIndicator = QLatin1String("# Untracked files:"); + + State s = None; + + const QRegExp filesPattern(QLatin1String("#\\t[^:]+:\\s+[^ ]+")); + Q_ASSERT(filesPattern.isValid()); + + const QStringList::const_iterator cend = lines.constEnd(); + for (QStringList::const_iterator it = lines.constBegin(); it != cend; ++it) { + const QString line = *it; + if (line.startsWith(branchIndicator)) { + d->panelInfo.branch = line.mid(branchIndicator.size() + 1); + } else { + if (line.startsWith(commitIndicator)) { + s = CommitFiles; + } else { + if (line.startsWith(notUpdatedIndicator)) { + s = NotUpdatedFiles; + } else { + if (line.startsWith(untrackedIndicator)) { + s = UntrackedFiles; + } else { + if (filesPattern.exactMatch(line)) { + const QString fileSpec = line.mid(2).simplified(); + switch (s) { + case CommitFiles: + d->commitFiles.push_back(fileSpec); + break; + case NotUpdatedFiles: + d->notUpdatedFiles.push_back(fileSpec); + break; + case UntrackedFiles: + d->untrackedFiles.push_back(fileSpec); + break; + case None: + break; + } + } + } + } + } + } + } + return !d->commitFiles.empty() || !d->notUpdatedFiles.empty() || !d->untrackedFiles.empty(); +} + +bool GitClient::getCommitData(const QString &workingDirectory, + QString *commitTemplate, + CommitData *d, + QString *errorMessage) +{ + d->clear(); + + // Find repo + const QString repoDirectory = GitClient::findRepositoryForDirectory(workingDirectory); + if (repoDirectory.isEmpty()) { + *errorMessage = tr("Unable to determine the repository for %1.").arg(workingDirectory); + return false; + } + + d->panelInfo.repository = repoDirectory; + + QDir gitDir(repoDirectory); + if (!gitDir.cd(QLatin1String(kGitDirectoryC))) { + *errorMessage = tr("The repository %1 is not initialized yet.").arg(repoDirectory); + return false; + } + + // Read description + const QString descriptionFile = gitDir.absoluteFilePath(QLatin1String("description")); + if (QFileInfo(descriptionFile).isFile()) { + QFile file(descriptionFile); + if (file.open(QIODevice::ReadOnly|QIODevice::Text)) + d->panelInfo.description = QString::fromLocal8Bit(file.readAll()).trimmed(); + } + + // Run status. Note that it has exitcode 1 if there are no added files. + QByteArray outputText; + QByteArray errorText; + const bool statusRc = synchronousGit(workingDirectory, QStringList(QLatin1String("status")), &outputText, &errorText); + if (!statusRc) { + // Something fatal + if (!outputText.contains(kBranchIndicatorC)) { + *errorMessage = tr("Unable to obtain the project status: %1").arg(QString::fromLocal8Bit(errorText)); + return false; + } + // All unchanged + if (outputText.contains("nothing to commit")) { + *errorMessage = tr("There are no modified files."); + return false; + } + } + + // Output looks like: + // # On branch [branchname] + // # Changes to be committed: + // # (use "git reset HEAD <file>..." to unstage) + // # + // # modified: somefile.cpp + // # new File: somenew.h + // # + // # Changed but not updated: + // # (use "git add <file>..." to update what will be committed) + // # + // # modified: someother.cpp + // # + // # Untracked files: + // # (use "git add <file>..." to include in what will be committed) + // # + // # list of files... + + const QStringList lines = QString::fromLocal8Bit(outputText).remove(QLatin1Char('\r')).split(QLatin1Char('\n')); + if (!parseFiles(lines, d)) { + *errorMessage = tr("Unable to parse the file output."); + return false; + } + + d->panelData.author = readConfigValue(workingDirectory, QLatin1String("user.name")); + d->panelData.email = readConfigValue(workingDirectory, QLatin1String("user.email")); + + // Get the commit template + const QString templateFilename = readConfigValue(workingDirectory, QLatin1String("commit.template")); + if (!templateFilename.isEmpty()) { + QFile templateFile(templateFilename); + if (templateFile.open(QIODevice::ReadOnly|QIODevice::Text)) { + *commitTemplate = QString::fromLocal8Bit(templateFile.readAll()); + } else { + qWarning("Unable to read commit template %s: %s", + qPrintable(templateFilename), + qPrintable(templateFile.errorString())); + } + } + return true; +} + +bool GitClient::addAndCommit(const QString &workingDirectory, + const GitSubmitEditorPanelData &data, + const QString &messageFile, + const QStringList &files) +{ + // Re-add all to make sure we have the latest changes + if (!synchronousAdd(workingDirectory, files)) + return false; + + // Do the final commit + QStringList args; + args << QLatin1String("commit") + << QLatin1String("-F") << QDir::toNativeSeparators(messageFile) + << QLatin1String("--author") << data.authorString(); + + QByteArray outputText; + QByteArray errorText; + const bool rc = synchronousGit(workingDirectory, args, &outputText, &errorText); + const QString message = rc ? + tr("Committed %n file(s).", 0, files.size()) : + tr("Unable to commit %n file(s): %1", 0, files.size()).arg(QString::fromLocal8Bit(errorText)); + + m_plugin->m_outputWindow->append(message); + m_plugin->m_outputWindow->popup(false); + return rc; +} + +void GitClient::pull(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("pull")), m_plugin->m_outputWindow, 0,true); +} + +void GitClient::push(const QString &workingDirectory) +{ + executeGit(workingDirectory, QStringList(QLatin1String("push")), m_plugin->m_outputWindow, 0,true); +} + +QString GitClient::readConfig(const QString &workingDirectory, const QStringList &configVar) +{ + QStringList arguments; + arguments << QLatin1String("config") << configVar; + + QByteArray outputText; + if (synchronousGit(workingDirectory, arguments, &outputText)) + return outputText; + return QString(); +} + +// Read a single-line config value, return trimmed +QString GitClient::readConfigValue(const QString &workingDirectory, const QString &configVar) +{ + return readConfig(workingDirectory, QStringList(configVar)).remove(QLatin1Char('\n')); +} + +GitCommand::GitCommand() +{ +} + +GitCommand::~GitCommand() +{ +} + +void GitCommand::execute(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment) +{ + if (Git::Constants::debug) + qDebug() << "GitCommand::execute" << workingDirectory << arguments; + + // For some reason QtConcurrent::run() only works on this + QFuture<void> task = QtConcurrent::run(this, &GitCommand::run + , arguments + , workingDirectory + , environment); + QString taskName = QLatin1String("Git ") + arguments[0]; + + Core::ICore *core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + core->progressManager()->addTask(task, taskName + , QLatin1String("Git.action") + , Core::ProgressManagerInterface::CloseOnSuccess); +} + +void GitCommand::run(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment) +{ + if (Git::Constants::debug) + qDebug() << "GitCommand::run" << workingDirectory << arguments; + QProcess process; + if (!workingDirectory.isEmpty()) + process.setWorkingDirectory(workingDirectory); + + ProjectExplorer::Environment env = environment; + if (env.toStringList().isEmpty()) + env = ProjectExplorer::Environment::systemEnvironment(); + process.setEnvironment(env.toStringList()); + + process.start(QLatin1String(kGitCommand), arguments); + if (!process.waitForFinished()) { + emit errorText(QLatin1String("Error: Git timed out")); + return; + } + + const QByteArray output = process.readAllStandardOutput(); + if (output.isEmpty()) { + if (arguments.at(0) == QLatin1String("diff")) + emit outputText(tr("The file does not differ from HEAD")); + } else { + emit outputData(output); + } + const QByteArray error = process.readAllStandardError(); + if (!error.isEmpty()) + emit errorText(QString::fromLocal8Bit(error)); + + // As it is used asynchronously, we need to delete ourselves + this->deleteLater(); +} diff --git a/src/plugins/git/gitclient.h b/src/plugins/git/gitclient.h new file mode 100644 index 0000000000..f91eefda66 --- /dev/null +++ b/src/plugins/git/gitclient.h @@ -0,0 +1,168 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITCLIENT_H +#define GITCLIENT_H + +#include <coreplugin/iversioncontrol.h> +#include <coreplugin/editormanager/ieditor.h> +#include <projectexplorer/environment.h> + +#include <QtCore/QString> +#include <QtCore/QStringList> + +QT_BEGIN_NAMESPACE +class QErrorMessage; +QT_END_NAMESPACE + +namespace Core { + class ICore; +} + +namespace VCSBase { + class VCSBaseEditor; +} + +namespace Git { +namespace Internal { + +class GitPlugin; +class GitOutputWindow; +class GitCommand; +struct CommitData; +struct GitSubmitEditorPanelData; + +class GitClient : public Core::IVersionControl +{ + Q_OBJECT +public: + explicit GitClient(GitPlugin *plugin, Core::ICore *core); + ~GitClient(); + bool vcsOpen(const QString &fileName); + bool vcsAdd(const QString&) {return false;} + bool vcsDelete(const QString&) {return false;} + bool managesDirectory(const QString&) const {return false;} + QString findTopLevelForDirectory(const QString&) const {return QString();} + + static QString findRepositoryForFile(const QString &fileName); + static QString findRepositoryForDirectory(const QString &dir); + + void diff(const QString &workingDirectory, + const QString &fileName); + void diff(const QString &workingDirectory, + const QStringList &fileNames); + + void status(const QString &workingDirectory); + void log(const QString &workingDirectory + , const QString &fileName); + void blame(const QString &workingDirectory + , const QString &fileName); + void showCommit(const QString &workingDirectory + , const QString &commit); + void checkout(const QString &workingDirectory + , const QString &file); + void hardReset(const QString &workingDirectory + , const QString &commit); + void addFile(const QString &workingDirectory + , const QString &fileName); + bool synchronousAdd(const QString &workingDirectory, + const QStringList &files); + void pull(const QString &workingDirectory); + void push(const QString &workingDirectory); + + QString readConfig(const QString &workingDirectory + , const QStringList &configVar); + + QString readConfigValue(const QString &workingDirectory, + const QString &configVar); + + bool getCommitData(const QString &workingDirectory, + QString *commitTemplate, + CommitData *d, + QString *errorMessage); + + bool addAndCommit(const QString &workingDirectory, + const GitSubmitEditorPanelData &data, + const QString &messageFile, + const QStringList &files); + +public slots: + void show(const QString &source, const QString &id); + +private: + VCSBase::VCSBaseEditor *createVCSEditor(const QString &kind, + QString title, + const QString &source, + bool setSourceCodec, + const char *registerDynamicProperty, + const QString &dynamicPropertyValue) const; + + + void executeGit(const QString &workingDirectory + , const QStringList &arguments + , GitOutputWindow *outputWindow + , VCSBase::VCSBaseEditor* editor = 0 + , bool outputToWindow = false); + + bool synchronousGit(const QString &workingDirectory + , const QStringList &arguments + , QByteArray* outputText = 0 + , QByteArray* errorText = 0); + + const QString m_msgWait; + GitPlugin *m_plugin; + Core::ICore *m_core; +}; + +class GitCommand : public QObject +{ + Q_OBJECT +public: + GitCommand(); + ~GitCommand(); + void execute(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment); + void run(const QStringList &arguments + , const QString &workingDirectory + , const ProjectExplorer::Environment &environment); + +Q_SIGNALS: + void outputData(const QByteArray&); + void outputText(const QString&); + void errorText(const QString&); +}; + + } +} + +#endif // GITCLIENT_H diff --git a/src/plugins/git/gitconstants.h b/src/plugins/git/gitconstants.h new file mode 100644 index 0000000000..a74c903031 --- /dev/null +++ b/src/plugins/git/gitconstants.h @@ -0,0 +1,58 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GIT_CONSTANTS_H +#define GIT_CONSTANTS_H + +namespace Git { + namespace Constants { + const char * const GIT_COMMAND_LOG_EDITOR_KIND = "Git Command Log Editor"; + const char * const GIT_LOG_EDITOR_KIND = "Git File Log Editor"; + const char * const GIT_BLAME_EDITOR_KIND = "Git Annotation Editor"; + const char * const GIT_DIFF_EDITOR_KIND = "Git Diff Editor"; + + const char * const C_GITSUBMITEDITOR = "Git Submit Editor"; + const char * const GITSUBMITEDITOR_KIND = "Git Submit Editor"; + const char * const SUBMIT_CURRENT = "Nokia.Git.SubmitCurrentLog"; + const char * const DIFF_SELECTED = "Nokia.Git.DiffSelectedFilesInLog"; + const char * const SUBMIT_MIMETYPE = "application/vnd.nokia.text.git.submit"; + + // TODO: For the moment, trust p4 is loaded... + const char * const ICON_SUBMIT = ":/trolltech.perforce/images/submit.png"; + const char * const ICON_DIFF = ":/trolltech.perforce/images/diff.png"; + + const char * const DIFF_FILE_INDICATOR = "--- "; + enum { debug = 0 }; + } +} + +#endif // GIT_CONSTANTS_H diff --git a/src/plugins/git/giteditor.cpp b/src/plugins/git/giteditor.cpp new file mode 100644 index 0000000000..3bf31da698 --- /dev/null +++ b/src/plugins/git/giteditor.cpp @@ -0,0 +1,145 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "giteditor.h" +#include "annotationhighlighter.h" +#include "gitconstants.h" +#include "gitplugin.h" +#include "gitclient.h" + +#include <vcsbase/diffhighlighter.h> +#include <coreplugin/editormanager/editormanager.h> + +#include <QtCore/QFileInfo> +#include <QtCore/QTextStream> +#include <QtCore/QSet> +#include <QtCore/QRegExp> +#include <QtCore/QDir> +#include <QtCore/QDebug> +#include <QtGui/QTextEdit> +#include <QtGui/QTextCursor> + +#define CHANGE_PATTERN_8C "[a-f0-9]{8,8}" +#define CHANGE_PATTERN_40C "[a-f0-9]{40,40}" + +namespace Git { +namespace Internal { + +// ------------ GitEditor +GitEditor::GitEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent) : + VCSBase::VCSBaseEditor(type, parent), + m_changeNumberPattern8(QLatin1String(CHANGE_PATTERN_8C)), + m_changeNumberPattern40(QLatin1String(CHANGE_PATTERN_40C)) +{ + Q_ASSERT(m_changeNumberPattern8.isValid()); + Q_ASSERT(m_changeNumberPattern40.isValid()); + if (Git::Constants::debug) + qDebug() << "GitEditor::GitEditor" << type->type << type->kind; +} + +QSet<QString> GitEditor::annotationChanges() const +{ + QSet<QString> changes; + const QString txt = toPlainText(); + if (txt.isEmpty()) + return changes; + // Hunt for first change number in annotation: "<change>:" + QRegExp r(QLatin1String("^("CHANGE_PATTERN_8C") ")); + Q_ASSERT(r.isValid()); + if (r.indexIn(txt) != -1) { + changes.insert(r.cap(1)); + r.setPattern(QLatin1String("\n("CHANGE_PATTERN_8C") ")); + Q_ASSERT(r.isValid()); + int pos = 0; + while ((pos = r.indexIn(txt, pos)) != -1) { + pos += r.matchedLength(); + changes.insert(r.cap(1)); + } + } + if (Git::Constants::debug) + qDebug() << "GitEditor::annotationChanges() returns #" << changes.size(); + return changes; +} + +QString GitEditor::changeUnderCursor(const QTextCursor &c) const +{ + QTextCursor cursor = c; + // Any number is regarded as change number. + cursor.select(QTextCursor::WordUnderCursor); + if (!cursor.hasSelection()) + return QString(); + const QString change = cursor.selectedText(); + if (Git::Constants::debug > 1) + qDebug() << "GitEditor:::changeUnderCursor:" << change; + if (m_changeNumberPattern8.exactMatch(change)) + return change; + if (m_changeNumberPattern40.exactMatch(change)) + return change; + return QString(); +} + +VCSBase::DiffHighlighter *GitEditor::createDiffHighlighter() const +{ + const QRegExp filePattern(QLatin1String("^[-+][-+][-+] [ab].*")); + return new VCSBase::DiffHighlighter(filePattern); +} + +VCSBase::BaseAnnotationHighlighter *GitEditor::createAnnotationHighlighter(const QSet<QString> &changes) const +{ + return new GitAnnotationHighlighter(changes); +} + +QString GitEditor::fileNameFromDiffSpecification(const QTextBlock &inBlock) const +{ + QString errorMessage; + // Check for "+++ b/src/plugins/git/giteditor.cpp" (blame and diff) + // Go back chunks. + const QString newFileIndicator = QLatin1String("+++ b/"); + for (QTextBlock block = inBlock; block.isValid(); block = block.previous()) { + QString diffFileName = block.text(); + if (diffFileName.startsWith(newFileIndicator)) { + diffFileName.remove(0, newFileIndicator.size()); + const QString fileOrDir = source(); + const QString repo = QFileInfo(fileOrDir).isDir() ? + GitClient::findRepositoryForDirectory(fileOrDir) : GitClient::findRepositoryForFile(fileOrDir); + const QString absPath = QDir(repo).absoluteFilePath(diffFileName); + if (Git::Constants::debug) + qDebug() << "fileNameFromDiffSpecification" << repo << diffFileName << absPath; + return absPath; + } + } + return QString(); +} + +} +} diff --git a/src/plugins/git/giteditor.h b/src/plugins/git/giteditor.h new file mode 100644 index 0000000000..87e71597dd --- /dev/null +++ b/src/plugins/git/giteditor.h @@ -0,0 +1,68 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITEDITOR_H +#define GITEDITOR_H + +#include <vcsbase/vcsbaseeditor.h> + +#include <QtCore/QRegExp> + +namespace Git { +namespace Internal { + +class GitPlugin; + +class GitEditor : public VCSBase::VCSBaseEditor +{ + Q_OBJECT + +public: + explicit GitEditor(const VCSBase::VCSBaseEditorParameters *type, + QWidget *parent); + +private: + virtual QSet<QString> annotationChanges() const; + virtual QString changeUnderCursor(const QTextCursor &) const; + virtual VCSBase::DiffHighlighter *createDiffHighlighter() const; + virtual VCSBase::BaseAnnotationHighlighter *createAnnotationHighlighter(const QSet<QString> &changes) const; + virtual QString fileNameFromDiffSpecification(const QTextBlock &diffFileName) const; + + const QRegExp m_changeNumberPattern8; + const QRegExp m_changeNumberPattern40; + GitPlugin *m_plugin; +}; + +} // namespace Git +} // namespace Internal + +#endif // GITEDITOR_H diff --git a/src/plugins/git/gitoutputwindow.cpp b/src/plugins/git/gitoutputwindow.cpp new file mode 100644 index 0000000000..e8210ed035 --- /dev/null +++ b/src/plugins/git/gitoutputwindow.cpp @@ -0,0 +1,122 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitoutputwindow.h" + +#include <QtCore/QTextCodec> +#include <QtGui/QKeyEvent> +#include <QtGui/QMouseEvent> +#include <QtGui/QMenu> +#include <QtGui/QAction> +#include <QtGui/QListWidget> + +using namespace Git::Internal; + +GitOutputWindow::GitOutputWindow() + : Core::IOutputPane() +{ + m_outputListWidget = new QListWidget; + m_outputListWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_outputListWidget->setFrameStyle(QFrame::NoFrame); + + m_outputListWidget->setWindowTitle(tr("Git Output")); +} + +GitOutputWindow::~GitOutputWindow() +{ + delete m_outputListWidget; +} + +QWidget *GitOutputWindow::outputWidget(QWidget *parent) +{ + m_outputListWidget->setParent(parent); + return m_outputListWidget; +} + +QString GitOutputWindow::name() const +{ + return tr("Git"); +} + +void GitOutputWindow::clearContents() +{ + m_outputListWidget->clear(); +} + +void GitOutputWindow::visibilityChanged(bool b) +{ + if (b) + m_outputListWidget->setFocus(); +} + +bool GitOutputWindow::hasFocus() +{ + return m_outputListWidget->hasFocus(); +} + +bool GitOutputWindow::canFocus() +{ + return false; +} + +void GitOutputWindow::setFocus() +{ +} + +void GitOutputWindow::setText(const QString &text) +{ + clearContents(); + append(text); +} + +void GitOutputWindow::append(const QString &text) +{ + const QStringList lines = text.split(QLatin1Char('\n')); + foreach (const QString &s, lines) + m_outputListWidget->addItem(s); + popup(); +} + +void GitOutputWindow::setData(const QByteArray &data) +{ + setText(QTextCodec::codecForLocale()->toUnicode(data)); +} + +void GitOutputWindow::appendData(const QByteArray &data) +{ + append(QTextCodec::codecForLocale()->toUnicode(data)); +} + +int GitOutputWindow::priorityInStatusBar() const +{ + return -1; +} diff --git a/src/plugins/git/gitoutputwindow.h b/src/plugins/git/gitoutputwindow.h new file mode 100644 index 0000000000..ed776c5df4 --- /dev/null +++ b/src/plugins/git/gitoutputwindow.h @@ -0,0 +1,79 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITOUTPUTWINDOW_H +#define GITOUTPUTWINDOW_H + +#include <coreplugin/ioutputpane.h> + +#include <QtGui/QAction> +#include <QtGui/QListWidget> +#include <QtGui/QListWidgetItem> + +namespace Git { +namespace Internal { + + +class GitOutputWindow : public Core::IOutputPane +{ + Q_OBJECT + +public: + GitOutputWindow(); + ~GitOutputWindow(); + + QWidget *outputWidget(QWidget *parent); + QList<QWidget*> toolBarWidgets(void) const { return QList<QWidget *>(); } + + QString name() const; + int priorityInStatusBar() const; + void clearContents(); + void visibilityChanged(bool visible); + + bool canFocus(); + bool hasFocus(); + void setFocus(); + +public slots: + void setText(const QString &text); + void append(const QString &text); + void setData(const QByteArray &data); + void appendData(const QByteArray &data); + +private: + QListWidget *m_outputListWidget; +}; + +} // namespace Internal +} // namespace Git + +#endif diff --git a/src/plugins/git/gitplugin.cpp b/src/plugins/git/gitplugin.cpp new file mode 100644 index 0000000000..cbb08cc05a --- /dev/null +++ b/src/plugins/git/gitplugin.cpp @@ -0,0 +1,717 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitplugin.h" +#include "gitclient.h" +#include "giteditor.h" +#include "gitconstants.h" +#include "changeselectiondialog.h" +#include "gitsubmiteditor.h" +#include "commitdata.h" + +#include <coreplugin/icore.h> +#include <coreplugin/coreconstants.h> +#include <coreplugin/filemanager.h> +#include <coreplugin/messagemanager.h> +#include <coreplugin/uniqueidmanager.h> +#include <coreplugin/actionmanager/actionmanagerinterface.h> +#include <coreplugin/editormanager/editormanager.h> +#include <vcsbase/basevcseditorfactory.h> +#include <vcsbase/vcsbaseeditor.h> +#include <vcsbase/basevcssubmiteditorfactory.h> + +#include <QtCore/qplugin.h> +#include <QtCore/QDebug> +#include <QtCore/QFileInfo> +#include <QtCore/QTemporaryFile> +#include <QtCore/QDir> +#include <QtGui/QAction> +#include <QtGui/QMenu> +#include <QtGui/QMessageBox> +#include <QtGui/QMainWindow> +#include <QtGui/QFileDialog> + +static const VCSBase::VCSBaseEditorParameters editorParameters[] = { +{ + VCSBase::RegularCommandOutput, + Git::Constants::GIT_COMMAND_LOG_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_commandlog", + "gitlog"}, +{ VCSBase::LogOutput, + Git::Constants::GIT_LOG_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_filelog", + "gitfilelog"}, +{ VCSBase::AnnotateOutput, + Git::Constants::GIT_BLAME_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "application/vnd.nokia.text.scs_git_annotation", + "gitsannotate"}, +{ VCSBase::DiffOutput, + Git::Constants::GIT_DIFF_EDITOR_KIND, + Core::Constants::C_GLOBAL, + "text/x-patch","diff"} +}; + +// Utility to find a parameter set by type +static inline const VCSBase::VCSBaseEditorParameters *findType(int ie) +{ + const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie); + return VCSBase::VCSBaseEditor::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et); +} + +using namespace Git; +using namespace Git::Internal; + +// CoreListener + +bool CoreListener::editorAboutToClose(Core::IEditor *editor) +{ + return m_plugin->editorAboutToClose(editor); +} + +// GitPlugin + +GitPlugin *GitPlugin::m_instance = 0; + +GitPlugin::GitPlugin() : + m_core(0), + m_diffAction(0), + m_diffProjectAction(0), + m_statusAction(0), + m_statusProjectAction(0), + m_logAction(0), + m_blameAction(0), + m_logProjectAction(0), + m_undoFileAction(0), + m_undoProjectAction(0), + m_showAction(0), + m_addAction(0), + m_commitAction(0), + m_pullAction(0), + m_pushAction(0), + m_submitCurrentAction(0), + m_diffSelectedFilesAction(0), + m_undoAction(0), + m_redoAction(0), + m_projectExplorer(0), + m_gitClient(0), + m_outputWindow(0), + m_changeSelectionDialog(0), + m_settingsPage(0), + m_coreListener(0), + m_submitEditorFactory(0), + m_changeTmpFile(0) +{ + Q_ASSERT(m_instance == 0); + m_instance = this; +} + +GitPlugin::~GitPlugin() +{ + if (m_outputWindow) { + removeObject(m_outputWindow); + delete m_outputWindow; + m_outputWindow = 0; + } + + if (m_settingsPage) { + removeObject(m_settingsPage); + delete m_settingsPage; + m_settingsPage = 0; + } + + if (!m_editorFactories.empty()) { + foreach(Core::IEditorFactory* pf, m_editorFactories) + removeObject(pf); + qDeleteAll(m_editorFactories); + } + + if (m_coreListener) { + removeObject(m_coreListener); + delete m_coreListener; + m_coreListener = 0; + } + + if (m_submitEditorFactory) { + removeObject(m_submitEditorFactory); + m_submitEditorFactory = 0; + } + + cleanChangeTmpFile(); + delete m_gitClient; + m_instance = 0; +} + +void GitPlugin::cleanChangeTmpFile() +{ + if (m_changeTmpFile) { + delete m_changeTmpFile; + m_changeTmpFile = 0; + } +} + +GitPlugin *GitPlugin::instance() +{ + return m_instance; +} + +static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = { + Git::Constants::SUBMIT_MIMETYPE, + Git::Constants::GITSUBMITEDITOR_KIND, + Git::Constants::C_GITSUBMITEDITOR, + Core::Constants::UNDO, + Core::Constants::REDO, + Git::Constants::SUBMIT_CURRENT, + Git::Constants::DIFF_SELECTED +}; + + +bool GitPlugin::initialize(const QStringList &arguments, QString *error_message) +{ + typedef VCSBase::VCSEditorFactory<GitEditor> GitEditorFactory; + typedef VCSBase::VCSSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory; + + Q_UNUSED(arguments); + Q_UNUSED(error_message); + + m_core = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + m_gitClient = new GitClient(this, m_core); + // Create the globalcontext list to register actions accordingly + QList<int> globalcontext; + globalcontext << m_core->uniqueIDManager()-> + uniqueIdentifier(Core::Constants::C_GLOBAL); + + // Create the output Window + m_outputWindow = new GitOutputWindow(); + addObject(m_outputWindow); + + // Create the settings Page + m_settingsPage = new SettingsPage(); + addObject(m_settingsPage); + + static const char *describeSlot = SLOT(show(QString,QString)); + const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters); + for (int i = 0; i < editorCount; i++) { + m_editorFactories.push_back(new GitEditorFactory(editorParameters + i, m_core, m_gitClient, describeSlot)); + addObject(m_editorFactories.back()); + } + + m_coreListener = new CoreListener(this); + addObject(m_coreListener); + + m_submitEditorFactory = new GitSubmitEditorFactory(&submitParameters); + addObject(m_submitEditorFactory); + + //register actions + Core::ActionManagerInterface *actionManager = m_core->actionManager(); + + Core::IActionContainer *toolsContainer = + actionManager->actionContainer(Core::Constants::M_TOOLS); + + Core::IActionContainer *gitContainer = + actionManager->createMenu(QLatin1String("Git")); + gitContainer->menu()->setTitle(tr("&Git")); + toolsContainer->addMenu(gitContainer); + + Core::ICommand *command; + QAction *tmpaction; + + m_diffAction = new QAction(tr("Diff current file"), this); + command = actionManager->registerAction(m_diffAction, "Git.Diff", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+D"))); + connect(m_diffAction, SIGNAL(triggered()), this, SLOT(diffCurrentFile())); + gitContainer->addAction(command); + + m_statusAction = new QAction(tr("File Status"), this); + command = actionManager->registerAction(m_statusAction, "Git.Status", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+S"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_statusAction, SIGNAL(triggered()), this, SLOT(statusFile())); + gitContainer->addAction(command); + + m_logAction = new QAction(tr("Log File"), this); + command = actionManager->registerAction(m_logAction, "Git.Log", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+L"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_logAction, SIGNAL(triggered()), this, SLOT(logFile())); + gitContainer->addAction(command); + + m_blameAction = new QAction(tr("Blame"), this); + command = actionManager->registerAction(m_blameAction, "Git.Blame", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+B"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_blameAction, SIGNAL(triggered()), this, SLOT(blameFile())); + gitContainer->addAction(command); + + m_undoFileAction = new QAction(tr("Undo Changes"), this); + command = actionManager->registerAction(m_undoFileAction, "Git.Undo", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+U"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_undoFileAction, SIGNAL(triggered()), this, SLOT(undoFileChanges())); + gitContainer->addAction(command); + + m_addAction = new QAction(tr("Add File"), this); + command = actionManager->registerAction(m_addAction, "Git.Add", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+A"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_addAction, SIGNAL(triggered()), this, SLOT(addFile())); + gitContainer->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = actionManager->registerAction(tmpaction, QLatin1String("Git.Sep.Project"), globalcontext); + gitContainer->addAction(command); + + m_diffProjectAction = new QAction(tr("Diff current project"), this); + command = actionManager->registerAction(m_diffProjectAction, "Git.DiffProject", globalcontext); + command->setDefaultKeySequence(QKeySequence("Alt+G,Alt+Shift+D")); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_diffProjectAction, SIGNAL(triggered()), this, SLOT(diffCurrentProject())); + gitContainer->addAction(command); + + m_statusProjectAction = new QAction(tr("Project status"), this); + command = actionManager->registerAction(m_statusProjectAction, "Git.StatusProject", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_statusProjectAction, SIGNAL(triggered()), this, SLOT(statusProject())); + gitContainer->addAction(command); + + m_logProjectAction = new QAction(tr("Log project"), this); + command = actionManager->registerAction(m_logProjectAction, "Git.LogProject", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+K"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_logProjectAction, SIGNAL(triggered()), this, SLOT(logProject())); + gitContainer->addAction(command); + + m_undoProjectAction = new QAction(tr("Undo Project Changes"), this); + command = actionManager->registerAction(m_undoProjectAction, "Git.UndoProject", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_undoProjectAction, SIGNAL(triggered()), this, SLOT(undoProjectChanges())); + gitContainer->addAction(command); + + tmpaction = new QAction(this); + tmpaction->setSeparator(true); + command = actionManager->registerAction(tmpaction, QLatin1String("Git.Sep.Global"), globalcontext); + gitContainer->addAction(command); + + m_showAction = new QAction(tr("Show commit..."), this); + command = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit())); + gitContainer->addAction(command); + + m_commitAction = new QAction(tr("Commit..."), this); + command = actionManager->registerAction(m_commitAction, "Git.Commit", globalcontext); + command->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C"))); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_commitAction, SIGNAL(triggered()), this, SLOT(startCommit())); + gitContainer->addAction(command); + + m_pullAction = new QAction(tr("Pull"), this); + command = actionManager->registerAction(m_pullAction, "Git.Pull", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_pullAction, SIGNAL(triggered()), this, SLOT(pull())); + gitContainer->addAction(command); + + m_pushAction = new QAction(tr("Push"), this); + command = actionManager->registerAction(m_pushAction, "Git.Push", globalcontext); + command->setAttribute(Core::ICommand::CA_UpdateText); + connect(m_pushAction, SIGNAL(triggered()), this, SLOT(push())); + gitContainer->addAction(command); + + // Submit editor + QList<int> submitContext; + submitContext.push_back(m_core->uniqueIDManager()->uniqueIdentifier(QLatin1String(Constants::C_GITSUBMITEDITOR))); + m_submitCurrentAction = new QAction(QIcon(Constants::ICON_SUBMIT), tr("Commit"), this); + command = actionManager->registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext); + // TODO + connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog())); + + m_diffSelectedFilesAction = new QAction(QIcon(Constants::ICON_DIFF), tr("Diff Selected Files"), this); + command = actionManager->registerAction(m_diffSelectedFilesAction, Constants::DIFF_SELECTED, submitContext); + + m_undoAction = new QAction(tr("&Undo"), this); + command = actionManager->registerAction(m_undoAction, Core::Constants::UNDO, submitContext); + + m_redoAction = new QAction(tr("&Redo"), this); + command = actionManager->registerAction(m_redoAction, Core::Constants::REDO, submitContext); + + // Ask for updates of our actions, in case context switches + connect(m_core, SIGNAL(contextChanged(Core::IContext *)), + this, SLOT(updateActions())); + connect(m_core->fileManager(), SIGNAL(currentFileChanged(const QString &)), + this, SLOT(updateActions())); + + return true; +} + +void GitPlugin::extensionsInitialized() +{ + m_projectExplorer = ExtensionSystem::PluginManager::instance()->getObject<ProjectExplorer::ProjectExplorerPlugin>(); +} + +bool GitPlugin::vcsOpen(const QString &fileName) +{ + Q_UNUSED(fileName); + return false; +} + +void GitPlugin::submitEditorDiff(const QStringList &files) +{ + if (files.empty()) + return; + m_gitClient->diff(m_submitRepository, files); +} + +void GitPlugin::diffCurrentFile() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->diff(workingDirectory, fileName); +} + +void GitPlugin::diffCurrentProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->diff(workingDirectory, QString()); +} + +QFileInfo GitPlugin::currentFile() +{ + QString fileName = m_core->fileManager()->currentFile(); + QFileInfo fileInfo(fileName); + return fileInfo; +} + +QString GitPlugin::getWorkingDirectory() +{ + QString workingDirectory; + if (m_projectExplorer && m_projectExplorer->currentNode()) { + workingDirectory = QFileInfo(m_projectExplorer->currentNode()->path()).absolutePath(); + } + if (Git::Constants::debug > 1) + qDebug() << Q_FUNC_INFO << "Project" << workingDirectory; + + if (workingDirectory.isEmpty()) + workingDirectory = QFileInfo(m_core->fileManager()->currentFile()).absolutePath(); + if (Git::Constants::debug > 1) + qDebug() << Q_FUNC_INFO << "file" << workingDirectory; + + if (workingDirectory.isEmpty()) { + m_outputWindow->clearContents(); + m_outputWindow->append(tr("Could not find working directory")); + m_outputWindow->popup(); + return QString(); + } + return workingDirectory; +} + +void GitPlugin::statusProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->status(workingDirectory); +} + +void GitPlugin::statusFile() +{ + m_gitClient->status(currentFile().absolutePath()); +} + +void GitPlugin::logFile() +{ + const QFileInfo fileInfo = currentFile(); + const QString fileName = fileInfo.fileName(); + const QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->log(workingDirectory, fileName); +} + +void GitPlugin::blameFile() +{ + const QFileInfo fileInfo = currentFile(); + const QString fileName = fileInfo.fileName(); + const QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->blame(workingDirectory, fileName); +} + +void GitPlugin::logProject() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->log(workingDirectory, QString()); +} + +void GitPlugin::undoFileChanges() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->checkout(workingDirectory, fileName); +} + +void GitPlugin::undoProjectChanges() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->hardReset(workingDirectory, QString()); +} + +void GitPlugin::addFile() +{ + QFileInfo fileInfo = currentFile(); + QString fileName = fileInfo.fileName(); + QString workingDirectory = fileInfo.absolutePath(); + m_gitClient->addFile(workingDirectory, fileName); +} + +void GitPlugin::startCommit() +{ + if (m_changeTmpFile) { + m_outputWindow->append(tr("Another submit is currently beeing executed.")); + m_outputWindow->popup(false); + return; + } + + // Find repository and get commit data + const QFileInfo currentFileInfo = currentFile(); + if (!currentFileInfo.exists()) + return; + + const QString workingDirectory = currentFileInfo.absolutePath(); + QString errorMessage, commitTemplate; + CommitData data; + if (!m_gitClient->getCommitData(workingDirectory, &commitTemplate, &data, &errorMessage)) { + m_outputWindow->append(errorMessage); + m_outputWindow->popup(false); + return; + } + + m_submitRepository = data.panelInfo.repository; + + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << data << commitTemplate; + + // Start new temp file with message template + QTemporaryFile *changeTmpFile = new QTemporaryFile(this); + changeTmpFile->setAutoRemove(true); + if (!changeTmpFile->open()) { + m_outputWindow->append(tr("Cannot create temporary file: %1").arg(changeTmpFile->errorString())); + delete changeTmpFile; + return; + } + m_changeTmpFile = changeTmpFile; + m_changeTmpFile->write(commitTemplate.toLocal8Bit()); + m_changeTmpFile->flush(); + // Keep the file alive, else it removes self and forgets + // its name + m_changeTmpFile->seek(0); + openSubmitEditor(m_changeTmpFile->fileName(), data); +} + +Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd) +{ + Core::IEditor *editor = m_core->editorManager()->openEditor(fileName, QLatin1String(Constants::GITSUBMITEDITOR_KIND)); + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << fileName << editor; + m_core->editorManager()->ensureEditorManagerVisible(); + GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor); + Q_ASSERT(submitEditor); + // The actions are for some reason enabled by the context switching + // mechanism. Disable them correctly. + m_submitCurrentAction->setEnabled(!cd.commitFiles.empty()); + m_diffSelectedFilesAction->setEnabled(false); + m_undoAction->setEnabled(false); + m_redoAction->setEnabled(false); + submitEditor->setCommitData(cd); + connect(submitEditor, SIGNAL(diffSelectedFiles(QStringList)), this, SLOT(submitEditorDiff(QStringList))); + return editor; +} + +void GitPlugin::submitCurrentLog() +{ + // Close the submit editor + QList<Core::IEditor*> editors; + editors.push_back(m_core->editorManager()->currentEditor()); + m_core->editorManager()->closeEditors(editors); +} + +bool GitPlugin::editorAboutToClose(Core::IEditor *iEditor) +{ + // Closing a submit editor? + if (!m_changeTmpFile || !iEditor || qstrcmp(iEditor->kind(), Constants::GITSUBMITEDITOR_KIND)) + return true; + Core::IFile *fileIFace = iEditor->file(); + const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(iEditor); + if (!fileIFace || !editor) + return true; + // Submit editor closing. Make it write out the commit message + // and retrieve files + const QFileInfo editorFile(fileIFace->fileName()); + const QFileInfo changeFile(m_changeTmpFile->fileName()); + // Paranoia! + if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath()) + return true; + // Prompt user. + const QMessageBox::StandardButton answer = QMessageBox::question(m_core->mainWindow(), tr("Closing git editor"), tr("Do you want to commit the change?"), + QMessageBox::Yes|QMessageBox::No|QMessageBox::Cancel, QMessageBox::Yes); + switch (answer) { + case QMessageBox::Cancel: + return false; // Keep editing and change file + case QMessageBox::No: + cleanChangeTmpFile(); + return true; // Cancel all + default: + break; + } + // Go ahead! + const QStringList fileList = editor->checkedFiles(); + if (Git::Constants::debug) + qDebug() << Q_FUNC_INFO << fileList; + if (!fileList.empty()) { + // get message & commit + m_core->fileManager()->blockFileChange(fileIFace); + fileIFace->save(); + m_core->fileManager()->unblockFileChange(fileIFace); + + m_gitClient->addAndCommit(m_submitRepository, + editor->panelData(), + m_changeTmpFile->fileName(), + fileList); + } + cleanChangeTmpFile(); + return true; +} + +void GitPlugin::pull() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->pull(workingDirectory); +} + +void GitPlugin::push() +{ + QString workingDirectory = getWorkingDirectory(); + if (workingDirectory.isEmpty()) + return; + m_gitClient->push(workingDirectory); +} + +void GitPlugin::updateActions() +{ + QFileInfo current = currentFile(); + const QString fileName = current.fileName(); + const QString currentDirectory = getWorkingDirectory(); + QString repository = m_gitClient->findRepositoryForFile(current.absoluteFilePath()); + // First check for file commands and if the current file is inside + // a Git-repository + m_diffAction->setText(tr("Diff %1").arg(fileName)); + m_statusAction->setText(tr("Status related to %1").arg(fileName)); + m_logAction->setText(tr("Log %1").arg(fileName)); + m_blameAction->setText(tr("Blame %1").arg(fileName)); + m_undoFileAction->setText(tr("Undo changes for %1").arg(fileName)); + m_addAction->setText(tr("Add %1").arg(fileName)); + if (repository.isEmpty()) { + // If the file is not in a repository, the corresponding project will + // be neither and we can disable everything and return + m_diffAction->setEnabled(false); + m_statusAction->setEnabled(false); + m_logAction->setEnabled(false); + m_blameAction->setEnabled(false); + m_undoFileAction->setEnabled(false); + m_addAction->setEnabled(false); + m_diffProjectAction->setEnabled(false); + m_diffProjectAction->setText(tr("Diff Project")); + m_statusProjectAction->setText(tr("Status Project")); + m_statusProjectAction->setEnabled(false); + m_logProjectAction->setText(tr("Log Project")); + m_logProjectAction->setEnabled(false); + return; + } else { + // We only know the file is in some repository, we do not know + // anything about any project so far. + m_diffAction->setEnabled(true); + m_statusAction->setEnabled(true); + m_logAction->setEnabled(true); + m_blameAction->setEnabled(true); + m_undoFileAction->setEnabled(true); + m_addAction->setEnabled(true); + } + + if (m_projectExplorer && m_projectExplorer->currentNode() + && m_projectExplorer->currentNode()->projectNode()) { + QString name = QFileInfo(m_projectExplorer->currentNode()->projectNode()->path()).baseName(); + m_diffProjectAction->setEnabled(true); + m_diffProjectAction->setText(tr("Diff Project %1").arg(name)); + m_statusProjectAction->setEnabled(true); + m_statusProjectAction->setText(tr("Status Project %1").arg(name)); + m_logProjectAction->setEnabled(true); + m_logProjectAction->setText(tr("Log Project %1").arg(name)); + } else { + m_diffProjectAction->setEnabled(false); + m_diffProjectAction->setText(tr("Diff Project")); + m_statusProjectAction->setEnabled(false); + m_statusProjectAction->setText(tr("Status Project")); + m_logProjectAction->setEnabled(false); + m_logProjectAction->setText(tr("Log Project")); + } +} + +void GitPlugin::showCommit() +{ + if (!m_changeSelectionDialog) + m_changeSelectionDialog = new ChangeSelectionDialog(); + + const QFileInfo currentInfo = currentFile(); + QString repositoryLocation = m_gitClient->findRepositoryForFile(currentInfo.absoluteFilePath()); + if (!repositoryLocation.isEmpty()) + m_changeSelectionDialog->m_ui.repositoryEdit->setText(repositoryLocation); + + if (m_changeSelectionDialog->exec() != QDialog::Accepted) + return; + const QString change = m_changeSelectionDialog->m_ui.changeNumberEdit->text(); + if (change .isEmpty()) + return; + + m_gitClient->show(m_changeSelectionDialog->m_ui.repositoryEdit->text(), change); +} + +Q_EXPORT_PLUGIN(GitPlugin) diff --git a/src/plugins/git/gitplugin.h b/src/plugins/git/gitplugin.h new file mode 100644 index 0000000000..c57c380fcf --- /dev/null +++ b/src/plugins/git/gitplugin.h @@ -0,0 +1,163 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITPLUGIN_H +#define GITPLUGIN_H + +#include "gitoutputwindow.h" +#include "settingspage.h" + +#include <coreplugin/editormanager/ieditorfactory.h> +#include <coreplugin/icorelistener.h> +#include <projectexplorer/ProjectExplorerInterfaces> +#include <extensionsystem/iplugin.h> + +#include <QtCore/QObject> +#include <QtCore/QProcess> +#include <QtCore/QList> + +QT_BEGIN_NAMESPACE +class QFile; +class QAction; +class QTemporaryFile; +QT_END_NAMESPACE + +namespace Core { + class IEditorFactory; + class ICore; +} + +namespace Git { +namespace Internal { + + class GitPlugin; + class GitClient; + class ChangeSelectionDialog; + class GitSubmitEditor; + struct CommitData; + +// Just a proxy for GitPlugin +class CoreListener : public Core::ICoreListener +{ + Q_OBJECT +public: + CoreListener(GitPlugin *plugin) : m_plugin(plugin) { } + bool editorAboutToClose(Core::IEditor *editor); + bool coreAboutToClose() { return true; } +private: + GitPlugin *m_plugin; +}; + +class GitPlugin : public ExtensionSystem::IPlugin +{ + Q_OBJECT + +public: + GitPlugin(); + ~GitPlugin(); + static GitPlugin *instance(); + + bool vcsOpen(const QString &fileName); + + bool initialize(const QStringList &arguments + , QString *error_message); + void extensionsInitialized(); + + QString getWorkingDirectory(); + +public slots: + void updateActions(); + bool editorAboutToClose(Core::IEditor *editor); + +private slots: + void diffCurrentFile(); + void diffCurrentProject(); + void submitEditorDiff(const QStringList &); + void submitCurrentLog(); + void statusFile(); + void statusProject(); + void logFile(); + void blameFile(); + void logProject(); + void undoFileChanges(); + void undoProjectChanges(); + void addFile(); + + void showCommit(); + void startCommit(); + void pull(); + void push(); + +private: + friend class GitClient; + QFileInfo currentFile(); + Core::IEditor *openSubmitEditor(const QString &fileName, const CommitData &cd); + void cleanChangeTmpFile(); + + static GitPlugin *m_instance; + Core::ICore *m_core; + QAction *m_diffAction; + QAction *m_diffProjectAction; + QAction *m_statusAction; + QAction *m_statusProjectAction; + QAction *m_logAction; + QAction *m_blameAction; + QAction *m_logProjectAction; + QAction *m_undoFileAction; + QAction *m_undoProjectAction; + QAction *m_showAction; + QAction *m_addAction; + QAction *m_commitAction; + QAction *m_pullAction; + QAction *m_pushAction; + + QAction *m_submitCurrentAction; + QAction *m_diffSelectedFilesAction; + QAction *m_undoAction; + QAction *m_redoAction; + + ProjectExplorer::ProjectExplorerPlugin *m_projectExplorer; + GitClient *m_gitClient; + GitOutputWindow *m_outputWindow; + ChangeSelectionDialog *m_changeSelectionDialog; + SettingsPage *m_settingsPage; + QList<Core::IEditorFactory*> m_editorFactories; + CoreListener *m_coreListener; + Core::IEditorFactory *m_submitEditorFactory; + QString m_submitRepository; + QTemporaryFile *m_changeTmpFile; +}; + +} // namespace Git +} // namespace Internal + +#endif diff --git a/src/plugins/git/gitsubmiteditor.cpp b/src/plugins/git/gitsubmiteditor.cpp new file mode 100644 index 0000000000..bf0d78af57 --- /dev/null +++ b/src/plugins/git/gitsubmiteditor.cpp @@ -0,0 +1,92 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitsubmiteditor.h" +#include "gitsubmiteditorwidget.h" +#include "gitconstants.h" +#include "commitdata.h" + +#include <QtCore/QDebug> + +namespace Git { +namespace Internal { + +GitSubmitEditor::GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent) : + VCSBaseSubmitEditor(parameters, new GitSubmitEditorWidget(parent)) +{ + setDisplayName(tr("Git Commit")); +} + +GitSubmitEditorWidget *GitSubmitEditor::submitEditorWidget() +{ + return static_cast<GitSubmitEditorWidget *>(widget()); +} + +QStringList GitSubmitEditor::vcsFileListToFileList(const QStringList &rawList) const +{ + QStringList rc; + foreach (const QString &rf, rawList) + rc.push_back(fileFromChangeLine(rf)); + return rc; +} + +void GitSubmitEditor::setCommitData(const CommitData &d) +{ + submitEditorWidget()->setPanelData(d.panelData); + submitEditorWidget()->setPanelInfo(d.panelInfo); + + // Commited: Checked, user cannot uncheck + addFiles(d.commitFiles, true, false); + // Not Updated: User can check + addFiles(d.notUpdatedFiles, false, true); + addFiles(d.untrackedFiles, false, true); +} + +GitSubmitEditorPanelData GitSubmitEditor::panelData() const +{ + return const_cast<GitSubmitEditor*>(this)->submitEditorWidget()->panelData(); +} + +QString GitSubmitEditor::fileFromChangeLine(const QString &line) +{ + QString rc = line; + // "modified: mainwindow.cpp" + const int index = rc.indexOf(QLatin1Char(':')); + if (index != -1) + rc.remove(0, index + 1); + const QChar blank(' '); + while (rc.startsWith(blank)) + rc.remove(0, 1); + return rc; +} +} +} diff --git a/src/plugins/git/gitsubmiteditor.h b/src/plugins/git/gitsubmiteditor.h new file mode 100644 index 0000000000..b424361a6a --- /dev/null +++ b/src/plugins/git/gitsubmiteditor.h @@ -0,0 +1,67 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITSUBMITEDITOR_H +#define GITSUBMITEDITOR_H + +#include <vcsbase/vcsbasesubmiteditor.h> + +namespace Git { +namespace Internal { + +class GitSubmitEditorWidget; +struct CommitData; +struct GitSubmitEditorPanelData; + +/* */ +class GitSubmitEditor : public VCSBase::VCSBaseSubmitEditor +{ + Q_OBJECT +public: + explicit GitSubmitEditor(const VCSBase::VCSBaseSubmitEditorParameters *parameters, QWidget *parent); + + void setCommitData(const CommitData &); + GitSubmitEditorPanelData panelData() const; + + static QString fileFromChangeLine(const QString &line); + +protected: + virtual QStringList vcsFileListToFileList(const QStringList &) const; + +private: + inline GitSubmitEditorWidget *submitEditorWidget(); +}; + +} // namespace Internal +} // namespace Git + +#endif // GITSUBMITEDITOR_H diff --git a/src/plugins/git/gitsubmiteditorwidget.cpp b/src/plugins/git/gitsubmiteditorwidget.cpp new file mode 100644 index 0000000000..aede10acd8 --- /dev/null +++ b/src/plugins/git/gitsubmiteditorwidget.cpp @@ -0,0 +1,69 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "gitsubmiteditorwidget.h" +#include "commitdata.h" + +namespace Git { +namespace Internal { + +GitSubmitEditorWidget::GitSubmitEditorWidget(QWidget *parent) : + Core::Utils::SubmitEditorWidget(parent), + m_gitSubmitPanel(new QWidget) +{ + m_gitSubmitPanelUi.setupUi(m_gitSubmitPanel); + insertTopWidget(m_gitSubmitPanel); +} + +void GitSubmitEditorWidget::setPanelInfo(const GitSubmitEditorPanelInfo &info) +{ + m_gitSubmitPanelUi.repositoryLabel->setText(info.repository); + m_gitSubmitPanelUi.descriptionLabel->setText(info.description); + m_gitSubmitPanelUi.branchLabel->setText(info.branch); +} + +GitSubmitEditorPanelData GitSubmitEditorWidget::panelData() const +{ + GitSubmitEditorPanelData rc; + rc.author = m_gitSubmitPanelUi.authorLineEdit->text(); + rc.email = m_gitSubmitPanelUi.emailLineEdit->text(); + return rc; +}; + +void GitSubmitEditorWidget::setPanelData(const GitSubmitEditorPanelData &data) +{ + m_gitSubmitPanelUi.authorLineEdit->setText(data.author); + m_gitSubmitPanelUi.emailLineEdit->setText(data.email); +} + +} +} diff --git a/src/plugins/git/gitsubmiteditorwidget.h b/src/plugins/git/gitsubmiteditorwidget.h new file mode 100644 index 0000000000..7a842f50ef --- /dev/null +++ b/src/plugins/git/gitsubmiteditorwidget.h @@ -0,0 +1,73 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef GITSUBMITEDITORWIDGET_H +#define GITSUBMITEDITORWIDGET_H + +#include "ui_gitsubmitpanel.h" +#include <utils/submiteditorwidget.h> + +namespace Git { +namespace Internal { + +struct GitSubmitEditorPanelInfo; +struct GitSubmitEditorPanelData; + +/* Submit editor widget with 2 additional panes: + * 1) Info with branch, description, etc + * 2) Data, with author and email to edit. + * The file contents is the submit message. + * The previously added files will be added 'checked' to the file list, the + * remaining un-added and untracked files will be added 'unchecked' for the + * user to click. */ + +class GitSubmitEditorWidget : public Core::Utils::SubmitEditorWidget +{ + +public: + explicit GitSubmitEditorWidget(QWidget *parent = 0); + + + GitSubmitEditorPanelData panelData() const; + void setPanelData(const GitSubmitEditorPanelData &data); + + void setPanelInfo(const GitSubmitEditorPanelInfo &info); + +private: + QWidget *m_gitSubmitPanel; + Ui::GitSubmitPanel m_gitSubmitPanelUi; +}; + +} // namespace Internal +} // namespace Perforce + +#endif // GITSUBMITEDITORWIDGET_H diff --git a/src/plugins/git/gitsubmitpanel.ui b/src/plugins/git/gitsubmitpanel.ui new file mode 100644 index 0000000000..8a42052a99 --- /dev/null +++ b/src/plugins/git/gitsubmitpanel.ui @@ -0,0 +1,101 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Git::Internal::GitSubmitPanel</class> + <widget class="QWidget" name="Git::Internal::GitSubmitPanel"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>201</width> + <height>210</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QGroupBox" name="infoGroup"> + <property name="title"> + <string>General Information</string> + </property> + <layout class="QFormLayout" name="infoFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="repositoryLabelLabel"> + <property name="text"> + <string>Repository:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="repositoryLabel"> + <property name="text"> + <string>repository</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="descriptionLabelLabel"> + <property name="text"> + <string>Description:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="descriptionLabel"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="text"> + <string>description</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="branchLabelLabel"> + <property name="text"> + <string>Branch:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="branchLabel"> + <property name="text"> + <string>branch</string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="editGroup"> + <property name="title"> + <string>Commit Information</string> + </property> + <layout class="QFormLayout" name="editFormLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="authorLabel"> + <property name="text"> + <string>Author:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="authorLineEdit"/> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="emailLabel"> + <property name="text"> + <string>Email:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLineEdit" name="emailLineEdit"/> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/src/plugins/git/settingspage.cpp b/src/plugins/git/settingspage.cpp new file mode 100644 index 0000000000..90f6371c0a --- /dev/null +++ b/src/plugins/git/settingspage.cpp @@ -0,0 +1,115 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#include "settingspage.h" + +#include <coreplugin/icore.h> +#include <extensionsystem/pluginmanager.h> + +#include <QtCore/QSettings> +#include <QtGui/QLineEdit> +#include <QtGui/QFileDialog> +#include <QtCore/QDebug> + +using namespace Git::Internal; + +static const char *groupC = "Git"; +static const char *sysEnvKeyC = "SysEnv"; +static const char *pathKeyC = "Path"; +static const char *logCountKeyC = "LogCount"; + +SettingsPage::SettingsPage() +{ + Core::ICore *coreIFace = ExtensionSystem::PluginManager::instance()->getObject<Core::ICore>(); + if (coreIFace) + m_settings = coreIFace->settings(); + + if (m_settings) { + m_settings->beginGroup(QLatin1String(groupC)); + m_adopt = m_settings->value(QLatin1String(sysEnvKeyC), true).toBool(); + m_path = m_settings->value(QLatin1String(pathKeyC), QString()).toString(); + m_logCount = m_settings->value(QLatin1String(logCountKeyC), 10).toInt(); + m_settings->endGroup(); + } +} + +QString SettingsPage::name() const +{ + return tr("General"); +} + +QString SettingsPage::category() const +{ + return QLatin1String("Git"); +} + +QString SettingsPage::trCategory() const +{ + return tr("Git"); +} + +QWidget *SettingsPage::createPage(QWidget *parent) +{ + QWidget *w = new QWidget(parent); + m_ui.setupUi(w); + m_ui.adoptCheckBox->setChecked(m_adopt); + m_ui.pathLineEdit->setText(m_path); + m_ui.logLineEdit->setText(QString::number(m_logCount)); + + connect(m_ui.adoptButton, SIGNAL(clicked()), this, SLOT(setSystemPath())); + return w; +} + +void SettingsPage::finished(bool accepted) +{ + if (!accepted) + return; + + m_adopt = m_ui.adoptCheckBox->isChecked(); + m_path = m_ui.pathLineEdit->text(); + m_logCount = m_ui.logLineEdit->text().toInt(); + + if (!m_settings) + return; + + m_settings->beginGroup(QLatin1String(groupC)); + m_settings->setValue(QLatin1String(sysEnvKeyC), m_adopt); + m_settings->setValue(QLatin1String(pathKeyC), m_path); + m_settings->setValue(QLatin1String(logCountKeyC), m_logCount); + m_settings->endGroup(); +} + +void SettingsPage::setSystemPath() +{ + m_path = qgetenv("PATH"); + m_ui.pathLineEdit->setText(m_path); +} diff --git a/src/plugins/git/settingspage.h b/src/plugins/git/settingspage.h new file mode 100644 index 0000000000..de627bd9ce --- /dev/null +++ b/src/plugins/git/settingspage.h @@ -0,0 +1,82 @@ +/*************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2008 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Qt Software Information (qt-info@nokia.com) +** +** +** Non-Open Source Usage +** +** Licensees may use this file in accordance with the Qt Beta Version +** License Agreement, Agreement version 2.2 provided with the Software or, +** alternatively, in accordance with the terms contained in a written +** agreement between you and Nokia. +** +** GNU General Public License Usage +** +** Alternatively, this file may be used under the terms of the GNU General +** Public License versions 2.0 or 3.0 as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL included in the packaging +** of this file. Please review the following information to ensure GNU +** General Public Licensing requirements will be met: +** +** http://www.fsf.org/licensing/licenses/info/GPLv2.html and +** http://www.gnu.org/copyleft/gpl.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt GPL Exception version +** 1.2, included in the file GPL_EXCEPTION.txt in this package. +** +***************************************************************************/ +#ifndef SETTINGSPAGE_H +#define SETTINGSPAGE_H + +#include <QtGui/QWidget> + +#include <coreplugin/dialogs/ioptionspage.h> + +#include "ui_settingspage.h" + +QT_BEGIN_NAMESPACE +class QSettings; +QT_END_NAMESPACE + +namespace Git { +namespace Internal { + +class SettingsPage : public Core::IOptionsPage +{ + Q_OBJECT + +public: + SettingsPage(); + + QString name() const; + QString category() const; + QString trCategory() const; + + QWidget *createPage(QWidget *parent); + void finished(bool accepted); + + bool adoptEnvironment() const { return m_adopt; } + int logCount() const { return m_logCount; } + QString path() const { return m_path; } + +private slots: + void setSystemPath(); + +private: + Ui_SettingsPage m_ui; + QSettings *m_settings; + + bool m_adopt; + QString m_path; + int m_logCount; +}; + +} //namespace Internal +} //namespace Git + +#endif diff --git a/src/plugins/git/settingspage.ui b/src/plugins/git/settingspage.ui new file mode 100644 index 0000000000..738413e676 --- /dev/null +++ b/src/plugins/git/settingspage.ui @@ -0,0 +1,135 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>Git::Internal::SettingsPage</class> + <widget class="QWidget" name="Git::Internal::SettingsPage"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>345</width> + <height>177</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <item> + <widget class="QCheckBox" name="adoptCheckBox"> + <property name="text"> + <string>Use System Environment</string> + </property> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="enabled"> + <bool>true</bool> + </property> + <property name="title"> + <string>Environment variables</string> + </property> + <property name="checkable"> + <bool>false</bool> + </property> + <layout class="QGridLayout" name="gridLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>PATH:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QLineEdit" name="pathLineEdit"/> + </item> + <item row="0" column="2"> + <widget class="QPushButton" name="adoptButton"> + <property name="text"> + <string>Adopt</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>52</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="1" column="1" colspan="2"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:8pt; font-weight:600;">Note</span><span style=" font-size:8pt;"> that Git needs Perl in the environment as well</span></p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Commit display count:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="logLineEdit"> + <property name="toolTip"> + <string>Note that huge amount of commits might take some time.</string> + </property> + </widget> + </item> + </layout> + </item> + <item> + <spacer> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>141</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <tabstops> + <tabstop>pathLineEdit</tabstop> + </tabstops> + <resources/> + <connections> + <connection> + <sender>adoptCheckBox</sender> + <signal>toggled(bool)</signal> + <receiver>groupBox</receiver> + <slot>setDisabled(bool)</slot> + <hints> + <hint type="sourcelabel"> + <x>144</x> + <y>33</y> + </hint> + <hint type="destinationlabel"> + <x>139</x> + <y>65</y> + </hint> + </hints> + </connection> + </connections> +</ui> |