gitplugin.cpp 60.8 KB
Newer Older
hjk's avatar
hjk committed
1
/****************************************************************************
con's avatar
con committed
2
**
3
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
hjk's avatar
hjk committed
4
** Contact: http://www.qt-project.org/legal
con's avatar
con committed
5
**
hjk's avatar
hjk committed
6
** This file is part of Qt Creator.
con's avatar
con committed
7
**
hjk's avatar
hjk committed
8 9 10 11 12 13 14
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.  For licensing terms and
** conditions see http://qt.digia.com/licensing.  For further information
** use the contact form at http://qt.digia.com/contact-us.
15
**
16
** GNU Lesser General Public License Usage
hjk's avatar
hjk committed
17 18 19 20 21 22 23 24 25
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 2.1 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL included in the
** packaging of this file.  Please review the following information to
** ensure the GNU Lesser General Public License version 2.1 requirements
** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
**
** In addition, as a special exception, Digia gives you certain additional
** rights.  These rights are described in the Digia Qt LGPL Exception
con's avatar
con committed
26 27
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
28
****************************************************************************/
hjk's avatar
hjk committed
29

con's avatar
con committed
30
#include "gitplugin.h"
hjk's avatar
hjk committed
31 32 33

#include "changeselectiondialog.h"
#include "commitdata.h"
con's avatar
con committed
34 35
#include "gitclient.h"
#include "gitconstants.h"
hjk's avatar
hjk committed
36
#include "giteditor.h"
con's avatar
con committed
37
#include "gitsubmiteditor.h"
hjk's avatar
hjk committed
38
#include "gitversioncontrol.h"
39
#include "branchdialog.h"
40
#include "remotedialog.h"
41
#include "clonewizard.h"
42
#include "gitorious/gitoriousclonewizard.h"
43
#include "stashdialog.h"
44
#include "settingspage.h"
45
#include "logchangedialog.h"
46
#include "mergetool.h"
Orgad Shaneh's avatar
Orgad Shaneh committed
47
#include "gitutils.h"
con's avatar
con committed
48

49
#include "gerrit/gerritplugin.h"
50

con's avatar
con committed
51 52
#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
53
#include <coreplugin/documentmanager.h>
54
#include <coreplugin/actionmanager/actionmanager.h>
55 56
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
57
#include <coreplugin/id.h>
Orgad Shaneh's avatar
Orgad Shaneh committed
58
#include <coreplugin/infobar.h>
con's avatar
con committed
59
#include <coreplugin/editormanager/editormanager.h>
60
#include <coreplugin/editormanager/ieditor.h>
61
#include <coreplugin/mimedatabase.h>
hjk's avatar
hjk committed
62 63

#include <utils/qtcassert.h>
64
#include <utils/parameteraction.h>
hjk's avatar
hjk committed
65

con's avatar
con committed
66
#include <vcsbase/basevcseditorfactory.h>
67
#include <vcsbase/submitfilemodel.h>
con's avatar
con committed
68 69
#include <vcsbase/vcsbaseeditor.h>
#include <vcsbase/basevcssubmiteditorfactory.h>
70
#include <vcsbase/vcsbaseoutputwindow.h>
71
#include <vcsbase/cleandialog.h>
Friedemann Kleint's avatar
Friedemann Kleint committed
72
#include <locator/commandlocator.h>
con's avatar
con committed
73

74 75 76 77
#include <QDebug>
#include <QDir>
#include <QFileInfo>
#include <QtPlugin>
hjk's avatar
hjk committed
78

79 80 81
#include <QAction>
#include <QFileDialog>
#include <QMessageBox>
82
#include <QScopedPointer>
con's avatar
con committed
83

Orgad Shaneh's avatar
Orgad Shaneh committed
84
static const unsigned minimumRequiredVersion = 0x010702;
85
static const char RC_GIT_MIME_XML[] = ":/git/Git.mimetypes.xml";
Orgad Shaneh's avatar
Orgad Shaneh committed
86

hjk's avatar
hjk committed
87
static const VcsBase::VcsBaseEditorParameters editorParameters[] = {
con's avatar
con committed
88
{
89
    VcsBase::OtherContent,
90 91
    Git::Constants::GIT_COMMAND_LOG_EDITOR_ID,
    Git::Constants::GIT_COMMAND_LOG_EDITOR_DISPLAY_NAME,
92
    Git::Constants::C_GIT_COMMAND_LOG_EDITOR,
Orgad Shaneh's avatar
Orgad Shaneh committed
93
    "text/vnd.qtcreator.git.commandlog"},
hjk's avatar
hjk committed
94
{   VcsBase::LogOutput,
95 96
    Git::Constants::GIT_LOG_EDITOR_ID,
    Git::Constants::GIT_LOG_EDITOR_DISPLAY_NAME,
97
    Git::Constants::C_GIT_LOG_EDITOR,
Orgad Shaneh's avatar
Orgad Shaneh committed
98
    "text/vnd.qtcreator.git.log"},
hjk's avatar
hjk committed
99
{   VcsBase::AnnotateOutput,
100 101
    Git::Constants::GIT_BLAME_EDITOR_ID,
    Git::Constants::GIT_BLAME_EDITOR_DISPLAY_NAME,
102
    Git::Constants::C_GIT_BLAME_EDITOR,
Orgad Shaneh's avatar
Orgad Shaneh committed
103
    "text/vnd.qtcreator.git.annotation"},
hjk's avatar
hjk committed
104
{   VcsBase::DiffOutput,
105 106
    Git::Constants::GIT_DIFF_EDITOR_ID,
    Git::Constants::GIT_DIFF_EDITOR_DISPLAY_NAME,
107
    Git::Constants::C_GIT_DIFF_EDITOR,
108
    "text/x-patch"},
109
{   VcsBase::OtherContent,
110 111 112 113
    Git::Constants::GIT_COMMIT_TEXT_EDITOR_ID,
    Git::Constants::GIT_COMMIT_TEXT_EDITOR_DISPLAY_NAME,
    Git::Constants::C_GIT_COMMIT_TEXT_EDITOR,
    "text/vnd.qtcreator.git.commit"},
114 115 116 117 118
{   VcsBase::OtherContent,
    Git::Constants::GIT_REBASE_EDITOR_ID,
    Git::Constants::GIT_REBASE_EDITOR_DISPLAY_NAME,
    Git::Constants::C_GIT_REBASE_EDITOR,
    "text/vnd.qtcreator.git.rebase"},
con's avatar
con committed
119 120
};

121 122
Q_DECLARE_METATYPE(Git::Internal::GitClientMemberFunc)

con's avatar
con committed
123 124 125 126 127 128 129 130
using namespace Git;
using namespace Git::Internal;

// GitPlugin

GitPlugin *GitPlugin::m_instance = 0;

GitPlugin::GitPlugin() :
Friedemann Kleint's avatar
Friedemann Kleint committed
131
    m_commandLocator(0),
con's avatar
con committed
132 133 134 135
    m_submitCurrentAction(0),
    m_diffSelectedFilesAction(0),
    m_undoAction(0),
    m_redoAction(0),
136
    m_menuAction(0),
137
    m_applyCurrentFilePatchAction(0),
con's avatar
con committed
138
    m_gitClient(0),
139
    m_submitActionTriggered(false)
con's avatar
con committed
140 141
{
    m_instance = this;
142 143 144 145 146
    const int mid = qRegisterMetaType<GitClientMemberFunc>();
    Q_UNUSED(mid)
    m_fileActions.reserve(10);
    m_projectActions.reserve(10);
    m_repositoryActions.reserve(15);
con's avatar
con committed
147 148 149 150
}

GitPlugin::~GitPlugin()
{
151
    cleanCommitMessageFile();
con's avatar
con committed
152 153 154 155
    delete m_gitClient;
    m_instance = 0;
}

156
void GitPlugin::cleanCommitMessageFile()
con's avatar
con committed
157
{
158 159 160
    if (!m_commitMessageFileName.isEmpty()) {
        QFile::remove(m_commitMessageFileName);
        m_commitMessageFileName.clear();
con's avatar
con committed
161 162 163
    }
}

164 165 166 167 168
bool GitPlugin::isCommitEditorOpen() const
{
    return !m_commitMessageFileName.isEmpty();
}

con's avatar
con committed
169 170 171 172 173
GitPlugin *GitPlugin::instance()
{
    return m_instance;
}

hjk's avatar
hjk committed
174
static const VcsBase::VcsBaseSubmitEditorParameters submitParameters = {
con's avatar
con committed
175
    Git::Constants::SUBMIT_MIMETYPE,
176 177
    Git::Constants::GITSUBMITEDITOR_ID,
    Git::Constants::GITSUBMITEDITOR_DISPLAY_NAME,
178 179
    Git::Constants::C_GITSUBMITEDITOR,
    VcsBase::VcsBaseSubmitEditorParameters::DiffRows
con's avatar
con committed
180 181
};

182 183
// Create a parameter action
ParameterActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
184
        GitPlugin::createParameterAction(Core::ActionContainer *ac,
185
                                         const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
186
                                         const Core::Id &id, const Core::Context &context,
187 188 189
                                         bool addToLocator)
{
    Utils::ParameterAction *action = new Utils::ParameterAction(defaultText, parameterText,
190 191
                                                                Utils::ParameterAction::EnabledWithParameter,
                                                                this);
Eike Ziller's avatar
Eike Ziller committed
192
    Core::Command *command = Core::ActionManager::registerAction(action, id, context);
193 194 195 196 197 198 199 200 201
    command->setAttribute(Core::Command::CA_UpdateText);
    ac->addAction(command);
    if (addToLocator)
        m_commandLocator->appendCommand(command);
    return ParameterActionCommandPair(action, command);
}

// Create an action to act on a file with a slot.
ParameterActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
202
        GitPlugin::createFileAction(Core::ActionContainer *ac,
203
                                    const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
204
                                    const Core::Id &id, const Core::Context &context, bool addToLocator,
205 206
                                    const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
207
    const ParameterActionCommandPair rc = createParameterAction(ac, defaultText, parameterText, id, context, addToLocator);
208 209 210 211 212 213 214
    m_fileActions.push_back(rc.first);
    connect(rc.first, SIGNAL(triggered()), this, pluginSlot);
    return rc;
}

// Create an action to act on a project with slot.
ParameterActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
215
        GitPlugin::createProjectAction(Core::ActionContainer *ac,
216
                                       const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
217
                                       const Core::Id &id, const Core::Context &context, bool addToLocator,
218 219
                                       const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
220
    const ParameterActionCommandPair rc = createParameterAction(ac, defaultText, parameterText, id, context, addToLocator);
221 222 223 224 225 226 227
    m_projectActions.push_back(rc.first);
    connect(rc.first, SIGNAL(triggered()), this, pluginSlot);
    return rc;
}

// Create an action to act on the repository
ActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
228
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
229
                                          const QString &text, const Core::Id &id,
230
                                          const Core::Context &context, bool addToLocator)
231 232
{
    QAction  *action = new QAction(text, this);
Eike Ziller's avatar
Eike Ziller committed
233
    Core::Command *command = Core::ActionManager::registerAction(action, id, context);
234 235
    if (ac)
        ac->addAction(command);
236 237 238 239 240 241 242 243
    m_repositoryActions.push_back(action);
    if (addToLocator)
        m_commandLocator->appendCommand(command);
    return ActionCommandPair(action, command);
}

// Create an action to act on the repository with slot
ActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
244
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
245
                                          const QString &text, const Core::Id &id,
246
                                          const Core::Context &context, bool addToLocator,
247 248
                                          const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
249
    const ActionCommandPair rc = createRepositoryAction(ac, text, id, context, addToLocator);
250 251 252 253 254 255 256
    connect(rc.first, SIGNAL(triggered()), this, pluginSlot);
    return rc;
}

// Action to act on the repository forwarded to a git client member function
// taking the directory. Store the member function as data on the action.
ActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
257
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
258
                                          const QString &text, const Core::Id &id,
259
                                          const Core::Context &context, bool addToLocator,
260 261 262
                                          GitClientMemberFunc func)
{
    // Set the member func as data and connect to generic slot
Eike Ziller's avatar
Eike Ziller committed
263
    const ActionCommandPair rc = createRepositoryAction(ac, text, id, context, addToLocator);
264 265 266 267 268
    rc.first->setData(qVariantFromValue(func));
    connect(rc.first, SIGNAL(triggered()), this, SLOT(gitClientMemberFuncRepositoryAction()));
    return rc;
}

269
bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
con's avatar
con committed
270
{
271 272
    Q_UNUSED(arguments)

hjk's avatar
hjk committed
273
    m_settings.readSettings(Core::ICore::settings());
274 275

    m_gitClient = new GitClient(&m_settings);
276

hjk's avatar
hjk committed
277 278
    typedef VcsBase::VcsEditorFactory<GitEditor> GitEditorFactory;
    typedef VcsBase::VcsSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory;
con's avatar
con committed
279

280
    initializeVcs(new GitVersionControl(m_gitClient));
con's avatar
con committed
281

282
    // Create the globalcontext list to register actions accordingly
283
    Core::Context globalcontext(Core::Constants::C_GLOBAL);
con's avatar
con committed
284 285

    // Create the settings Page
286
    addAutoReleasedObject(new SettingsPage());
con's avatar
con committed
287 288

    static const char *describeSlot = SLOT(show(QString,QString));
hjk's avatar
hjk committed
289
    const int editorCount = sizeof(editorParameters)/sizeof(VcsBase::VcsBaseEditorParameters);
290 291
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new GitEditorFactory(editorParameters + i, m_gitClient, describeSlot));
con's avatar
con committed
292

293 294
    addAutoReleasedObject(new GitSubmitEditorFactory(&submitParameters));
    addAutoReleasedObject(new CloneWizard);
295
    addAutoReleasedObject(new Gitorious::Internal::GitoriousCloneWizard);
296

Friedemann Kleint's avatar
Friedemann Kleint committed
297
    const QString prefix = QLatin1String("git");
298
    m_commandLocator = new Locator::CommandLocator("Git", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
299 300
    addAutoReleasedObject(m_commandLocator);

con's avatar
con committed
301
    //register actions
302
    Core::ActionContainer *toolsContainer =
Eike Ziller's avatar
Eike Ziller committed
303
        Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
con's avatar
con committed
304

Eike Ziller's avatar
Eike Ziller committed
305
    Core::ActionContainer *gitContainer = Core::ActionManager::createMenu("Git");
con's avatar
con committed
306 307
    gitContainer->menu()->setTitle(tr("&Git"));
    toolsContainer->addMenu(gitContainer);
308
    m_menuAction = gitContainer->menu()->menuAction();
309

Petar Perisin's avatar
Petar Perisin committed
310 311 312 313
    /*  "Current File" menu */
    Core::ActionContainer *currentFileMenu = Core::ActionManager::createMenu(Core::Id("Git.CurrentFileMenu"));
    currentFileMenu->menu()->setTitle(tr("Current &File"));
    gitContainer->addMenu(currentFileMenu);
314

Petar Perisin's avatar
Petar Perisin committed
315 316
    ParameterActionCommandPair parameterActionCommand
            = createFileAction(currentFileMenu,
317
                               tr("Diff Current File"), tr("Diff of \"%1\""),
hjk's avatar
hjk committed
318
                               Core::Id("Git.Diff"), globalcontext, true,
319
                               SLOT(diffCurrentFile()));
320
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+D") : tr("Alt+G,Alt+D")));
321

322
    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
323
            = createFileAction(currentFileMenu,
324
                               tr("Log Current File"), tr("Log of \"%1\""),
hjk's avatar
hjk committed
325
                               Core::Id("Git.Log"), globalcontext, true, SLOT(logFile()));
326
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+L") : tr("Alt+G,Alt+L")));
327

Petar Perisin's avatar
Petar Perisin committed
328 329 330 331 332 333 334
    parameterActionCommand
                = createFileAction(currentFileMenu,
                                   tr("Blame Current File"), tr("Blame for \"%1\""),
                                   Core::Id("Git.Blame"),
                                   globalcontext, true, SLOT(blameFile()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+B") : tr("Alt+G,Alt+B")));

335
    // ------
Petar Perisin's avatar
Petar Perisin committed
336
    currentFileMenu->addSeparator(globalcontext);
337 338

    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
339
            = createFileAction(currentFileMenu,
340
                               tr("Stage File for Commit"), tr("Stage \"%1\" for Commit"),
hjk's avatar
hjk committed
341
                               Core::Id("Git.Stage"), globalcontext, true, SLOT(stageFile()));
342
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+A") : tr("Alt+G,Alt+A")));
343 344

    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
345
            = createFileAction(currentFileMenu,
346
                               tr("Unstage File from Commit"), tr("Unstage \"%1\" from Commit"),
hjk's avatar
hjk committed
347
                               Core::Id("Git.Unstage"), globalcontext, true, SLOT(unstageFile()));
348

349
    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
350
            = createFileAction(currentFileMenu,
351
                               tr("Undo Unstaged Changes"), tr("Undo Unstaged Changes for \"%1\""),
hjk's avatar
hjk committed
352
                               Core::Id("Git.UndoUnstaged"), globalcontext,
353 354 355
                               true, SLOT(undoUnstagedFileChanges()));

    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
356
            = createFileAction(currentFileMenu,
357
                               tr("Undo Uncommitted Changes"), tr("Undo Uncommitted Changes for \"%1\""),
hjk's avatar
hjk committed
358
                               Core::Id("Git.Undo"), globalcontext,
359
                               true, SLOT(undoFileChanges()));
360
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+U") : tr("Alt+G,Alt+U")));
361

Petar Perisin's avatar
Petar Perisin committed
362 363
    /* \"Current File" menu */

364
    // ------------
Petar Perisin's avatar
Petar Perisin committed
365 366 367 368 369

    /*  "Current Project" menu */
    Core::ActionContainer *currentProjectMenu = Core::ActionManager::createMenu(Core::Id("Git.CurrentProjectMenu"));
    currentProjectMenu->menu()->setTitle(tr("Current &Project"));
    gitContainer->addMenu(currentProjectMenu);
con's avatar
con committed
370

371
    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
372
            = createProjectAction(currentProjectMenu,
373
                                  tr("Diff Current Project"), tr("Diff Project \"%1\""),
hjk's avatar
hjk committed
374
                                  Core::Id("Git.DiffProject"),
375 376
                                  globalcontext, true,
                                  SLOT(diffCurrentProject()));
377
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+Shift+D") : tr("Alt+G,Alt+Shift+D")));
378 379

    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
380
            = createProjectAction(currentProjectMenu,
381
                                  tr("Log Project"), tr("Log Project \"%1\""),
hjk's avatar
hjk committed
382
                                  Core::Id("Git.LogProject"), globalcontext, true,
383
                                  SLOT(logProject()));
384
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+K") : tr("Alt+G,Alt+K")));
385 386

    parameterActionCommand
Petar Perisin's avatar
Petar Perisin committed
387
                = createProjectAction(currentProjectMenu,
388
                                      tr("Clean Project..."), tr("Clean Project \"%1\"..."),
hjk's avatar
hjk committed
389
                                      Core::Id("Git.CleanProject"), globalcontext,
390
                                      true, SLOT(cleanProject()));
con's avatar
con committed
391

Petar Perisin's avatar
Petar Perisin committed
392
    /* \"Current Project" menu */
393 394

    // --------------
con's avatar
con committed
395

Petar Perisin's avatar
Petar Perisin committed
396 397 398 399 400 401
    /*  "Local Repository" menu */
    Core::ActionContainer *localRepositoryMenu = Core::ActionManager::createMenu(Core::Id("Git.LocalRepositoryMenu"));
    localRepositoryMenu->menu()->setTitle(tr("&Local Repository"));
    gitContainer->addMenu(localRepositoryMenu);

    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
402
                           tr("Diff"), Core::Id("Git.DiffRepository"),
403
                           globalcontext, true, SLOT(diffRepository()));
404

Petar Perisin's avatar
Petar Perisin committed
405
    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
406
                           tr("Log"), Core::Id("Git.LogRepository"),
407 408
                           globalcontext, true,
                           SLOT(logRepository()));
409

Petar Perisin's avatar
Petar Perisin committed
410 411 412 413 414
    createRepositoryAction(localRepositoryMenu,
                           tr("Clean..."), Core::Id("Git.CleanRepository"),
                           globalcontext, true, SLOT(cleanRepository()));

    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
415
                           tr("Status"), Core::Id("Git.StatusRepository"),
416
                           globalcontext, true, &GitClient::status);
417

Petar Perisin's avatar
Petar Perisin committed
418 419
    // --------------
    localRepositoryMenu->addSeparator(globalcontext);
con's avatar
con committed
420

Petar Perisin's avatar
Petar Perisin committed
421 422 423 424
    ActionCommandPair actionCommand = createRepositoryAction(localRepositoryMenu,
                                                             tr("Commit..."), Core::Id("Git.Commit"),
                                                             globalcontext, true, SLOT(startCommit()));
    actionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+C") : tr("Alt+G,Alt+C")));
425

Petar Perisin's avatar
Petar Perisin committed
426 427 428
    createRepositoryAction(localRepositoryMenu,
                           tr("Amend Last Commit..."), Core::Id("Git.AmendCommit"),
                           globalcontext, true, SLOT(startAmendCommit()));
429 430 431 432

    createRepositoryAction(localRepositoryMenu,
                           tr("Fixup Previous Commit..."), Core::Id("Git.FixupCommit"),
                           globalcontext, true, SLOT(startFixupCommit()));
433 434
    // --------------
    localRepositoryMenu->addSeparator(globalcontext);
435

Petar Perisin's avatar
Petar Perisin committed
436 437
    createRepositoryAction(localRepositoryMenu,
                           tr("Reset..."), Core::Id("Git.Reset"),
Orgad Shaneh's avatar
Orgad Shaneh committed
438
                           globalcontext, true, SLOT(resetRepository()));
439

Orgad Shaneh's avatar
Orgad Shaneh committed
440
    createRepositoryAction(localRepositoryMenu,
441
                           tr("Interactive Rebase..."), Core::Id("Git.InteractiveRebase"),
Orgad Shaneh's avatar
Orgad Shaneh committed
442 443
                           globalcontext, true, SLOT(startRebase()));

Petar Perisin's avatar
Petar Perisin committed
444 445 446 447
    m_submoduleUpdateAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Update Submodules"), Core::Id("Git.SubmoduleUpdate"),
                                   globalcontext, true, SLOT(updateSubmodules())).first;
Petar Perisin's avatar
Petar Perisin committed
448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481
    m_abortMergeAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Abort Merge"), Core::Id("Git.MergeAbort"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_abortRebaseAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Abort Rebase"), Core::Id("Git.RebaseAbort"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_abortCherryPickAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Abort Cherry Pick"), Core::Id("Git.CherryPickAbort"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_abortRevertAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Abort Revert"), Core::Id("Git.RevertAbort"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_continueRebaseAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Continue Rebase"), Core::Id("Git.RebaseContinue"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_continueCherryPickAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Continue Cherry Pick"), Core::Id("Git.CherryPickContinue"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;

    m_continueRevertAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Continue Revert"), Core::Id("Git.RevertContinue"),
                                   globalcontext, true, SLOT(continueOrAbortCommand())).first;
Petar Perisin's avatar
Petar Perisin committed
482

483
    // --------------
Petar Perisin's avatar
Petar Perisin committed
484
    localRepositoryMenu->addSeparator(globalcontext);
485

Petar Perisin's avatar
Petar Perisin committed
486
    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
487
                           tr("Branches..."), Core::Id("Git.BranchList"),
Yuchen Deng's avatar
Yuchen Deng committed
488
                           globalcontext, true, SLOT(branchList()));
489 490

    // --------------
Petar Perisin's avatar
Petar Perisin committed
491
    localRepositoryMenu->addSeparator(globalcontext);
492

Petar Perisin's avatar
Petar Perisin committed
493
    // "Patch" menu
Eike Ziller's avatar
Eike Ziller committed
494
    Core::ActionContainer *patchMenu = Core::ActionManager::createMenu(Core::Id("Git.PatchMenu"));
Petar Perisin's avatar
Petar Perisin committed
495 496
    patchMenu->menu()->setTitle(tr("&Patch"));
    localRepositoryMenu->addMenu(patchMenu);
497 498 499

    // Apply current file as patch is handled specially.
    parameterActionCommand =
Eike Ziller's avatar
Eike Ziller committed
500
            createParameterAction(patchMenu,
501
                                  tr("Apply from Editor"), tr("Apply \"%1\""),
hjk's avatar
hjk committed
502
                                  Core::Id("Git.ApplyCurrentFilePatch"),
503 504 505 506 507
                                  globalcontext, true);
    m_applyCurrentFilePatchAction = parameterActionCommand.first;
    connect(m_applyCurrentFilePatchAction, SIGNAL(triggered()), this,
            SLOT(applyCurrentFilePatch()));

Eike Ziller's avatar
Eike Ziller committed
508
    createRepositoryAction(patchMenu,
hjk's avatar
hjk committed
509
                           tr("Apply from File..."), Core::Id("Git.ApplyPatch"),
510 511
                           globalcontext, true, SLOT(promptApplyPatch()));

Petar Perisin's avatar
Petar Perisin committed
512
    // "Stash" menu
Eike Ziller's avatar
Eike Ziller committed
513
    Core::ActionContainer *stashMenu = Core::ActionManager::createMenu(Core::Id("Git.StashMenu"));
Petar Perisin's avatar
Petar Perisin committed
514 515
    stashMenu->menu()->setTitle(tr("&Stash"));
    localRepositoryMenu->addMenu(stashMenu);
516

Eike Ziller's avatar
Eike Ziller committed
517
    createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
518
                           tr("Stashes..."), Core::Id("Git.StashList"),
519 520
                           globalcontext, false, SLOT(stashList()));

521
    stashMenu->addSeparator(globalcontext);
522

Petar Perisin's avatar
Petar Perisin committed
523 524 525
    actionCommand = createRepositoryAction(stashMenu,
                                           tr("Stash"), Core::Id("Git.Stash"),
                                           globalcontext, true, SLOT(stash()));
526 527
    actionCommand.first->setToolTip(tr("Saves the current state of your work and resets the repository."));

Eike Ziller's avatar
Eike Ziller committed
528
    actionCommand = createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
529
                                           tr("Take Snapshot..."), Core::Id("Git.StashSnapshot"),
530 531 532
                                           globalcontext, true, SLOT(stashSnapshot()));
    actionCommand.first->setToolTip(tr("Saves the current state of your work."));

533
    stashMenu->addSeparator(globalcontext);
534

Eike Ziller's avatar
Eike Ziller committed
535
    actionCommand = createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
536
                                           tr("Stash Pop"), Core::Id("Git.StashPop"),
537 538 539
                                           globalcontext, true, &GitClient::stashPop);
    actionCommand.first->setToolTip(tr("Restores changes saved to the stash list using \"Stash\"."));

Petar Perisin's avatar
Petar Perisin committed
540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565

    /* \"Local Repository" menu */

    // --------------

    /*  "Remote Repository" menu */
    Core::ActionContainer *remoteRepositoryMenu = Core::ActionManager::createMenu(Core::Id("Git.RemoteRepositoryMenu"));
    remoteRepositoryMenu->menu()->setTitle(tr("&Remote Repository"));
    gitContainer->addMenu(remoteRepositoryMenu);

    createRepositoryAction(remoteRepositoryMenu,
                           tr("Fetch"), Core::Id("Git.Fetch"),
                           globalcontext, true, SLOT(fetch()));

    createRepositoryAction(remoteRepositoryMenu,
                           tr("Pull"), Core::Id("Git.Pull"),
                           globalcontext, true, SLOT(pull()));

    actionCommand = createRepositoryAction(remoteRepositoryMenu,
                                           tr("Push"), Core::Id("Git.Push"),
                                           globalcontext, true, SLOT(push()));

    // --------------
    remoteRepositoryMenu->addSeparator(globalcontext);

    // "Subversion" menu
Eike Ziller's avatar
Eike Ziller committed
566
    Core::ActionContainer *subversionMenu = Core::ActionManager::createMenu(Core::Id("Git.Subversion"));
Petar Perisin's avatar
Petar Perisin committed
567 568
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    remoteRepositoryMenu->addMenu(subversionMenu);
569

Eike Ziller's avatar
Eike Ziller committed
570
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
571
                           tr("Log"), Core::Id("Git.Subversion.Log"),
572 573
                           globalcontext, false, &GitClient::subversionLog);

Eike Ziller's avatar
Eike Ziller committed
574
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
575
                           tr("Fetch"), Core::Id("Git.Subversion.Fetch"),
576 577
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

Petar Perisin's avatar
Petar Perisin committed
578 579
    // --------------
    remoteRepositoryMenu->addSeparator(globalcontext);
580

Petar Perisin's avatar
Petar Perisin committed
581 582 583
    createRepositoryAction(remoteRepositoryMenu,
                           tr("Manage Remotes..."), Core::Id("Git.RemoteList"),
                           globalcontext, false, SLOT(remoteList()));
584

Petar Perisin's avatar
Petar Perisin committed
585
    /* \"Remote Repository" menu */
Robert Loehning's avatar
Robert Loehning committed
586

Petar Perisin's avatar
Petar Perisin committed
587
    // --------------
588

589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611
    /*  Actions only in locator */
    createRepositoryAction(0, tr("Show..."), Core::Id("Git.Show"),
                           globalcontext, true, SLOT(startChangeRelatedAction()));

    createRepositoryAction(0, tr("Revert..."), Core::Id("Git.Revert"),
                           globalcontext, true, SLOT(startChangeRelatedAction()));

    createRepositoryAction(0, tr("Cherry Pick..."), Core::Id("Git.CherryPick"),
                           globalcontext, true, SLOT(startChangeRelatedAction()));

    createRepositoryAction(0, tr("Checkout..."), Core::Id("Git.Checkout"),
                           globalcontext, true, SLOT(startChangeRelatedAction()));

    createRepositoryAction(0, tr("Rebase..."), Core::Id("Git.Rebase"),
                           globalcontext, true, SLOT(branchList()));

    createRepositoryAction(0, tr("Merge..."), Core::Id("Git.Merge"),
                           globalcontext, true, SLOT(branchList()));

    /*  \Actions only in locator */

    // --------------

Petar Perisin's avatar
Petar Perisin committed
612 613 614 615
    /*  "Git Tools" menu */
    Core::ActionContainer *gitToolsMenu = Core::ActionManager::createMenu(Core::Id("Git.GitToolsMenu"));
    gitToolsMenu->menu()->setTitle(tr("Git &Tools"));
    gitContainer->addMenu(gitToolsMenu);
616

Petar Perisin's avatar
Petar Perisin committed
617 618 619
    createRepositoryAction(gitToolsMenu,
                           tr("Gitk"), Core::Id("Git.LaunchGitK"),
                           globalcontext, true, &GitClient::launchGitK);
620

Petar Perisin's avatar
Petar Perisin committed
621 622 623 624 625 626 627 628 629 630 631 632 633
    parameterActionCommand
            = createFileAction(gitToolsMenu,
                               tr("Gitk Current File"), tr("Gitk of \"%1\""),
                               Core::Id("Git.GitkFile"), globalcontext, true, SLOT(gitkForCurrentFile()));

    parameterActionCommand
            = createFileAction(gitToolsMenu,
                               tr("Gitk for folder of Current File"), tr("Gitk for folder of \"%1\""),
                               Core::Id("Git.GitkFolder"), globalcontext, true, SLOT(gitkForCurrentFolder()));

    // --------------
    gitToolsMenu->addSeparator(globalcontext);

Petar Perisin's avatar
Petar Perisin committed
634 635 636 637
    m_repositoryBrowserAction
            = createRepositoryAction(gitToolsMenu,
                                     tr("Repository Browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;
638

Petar Perisin's avatar
Petar Perisin committed
639 640 641 642
    m_mergeToolAction =
            createRepositoryAction(gitToolsMenu,
                                   tr("Merge Tool"), Core::Id("Git.MergeTool"),
                                   globalcontext, true, SLOT(startMergeTool())).first;
643

Petar Perisin's avatar
Petar Perisin committed
644 645 646
    /* \"Git Tools" menu */

    // --------------
647
    gitContainer->addSeparator(globalcontext);
648

649 650 651 652 653
    QAction *changesAction = new QAction(tr("Actions on Commits..."), this);
    Core::Command *changesCommand = Core::ActionManager::registerAction(changesAction, "Git.ChangeActions", globalcontext);
    connect(changesAction, SIGNAL(triggered()), this, SLOT(startChangeRelatedAction()));
    gitContainer->addAction(changesCommand);

654 655 656
    QAction *repositoryAction = new QAction(tr("Create Repository..."), this);
    Core::Command *createRepositoryCommand = Core::ActionManager::registerAction(repositoryAction, "Git.CreateRepository", globalcontext);
    connect(repositoryAction, SIGNAL(triggered()), this, SLOT(createRepository()));
Petar Perisin's avatar
Petar Perisin committed
657 658
    gitContainer->addAction(createRepositoryCommand);

659 660 661 662
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
663
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
664
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
665
                                                    Core::Id("Git.Snapshot.").withSuffix(i),
666
                                                    globalcontext);
667
            gitContainer->addAction(tCommand);
668 669 670
        }
    }

con's avatar
con committed
671
    // Submit editor
672
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
673
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
674
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
675
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
676 677
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

hjk's avatar
hjk committed
678
    m_diffSelectedFilesAction = new QAction(VcsBase::VcsBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
Eike Ziller's avatar
Eike Ziller committed
679
    command = Core::ActionManager::registerAction(m_diffSelectedFilesAction, Constants::DIFF_SELECTED, submitContext);
con's avatar
con committed
680 681

    m_undoAction = new QAction(tr("&Undo"), this);
Eike Ziller's avatar
Eike Ziller committed
682
    command = Core::ActionManager::registerAction(m_undoAction, Core::Constants::UNDO, submitContext);
con's avatar
con committed
683 684

    m_redoAction = new QAction(tr("&Redo"), this);
Eike Ziller's avatar
Eike Ziller committed
685
    command = Core::ActionManager::registerAction(m_redoAction, Core::Constants::REDO, submitContext);
con's avatar
con committed
686

687

688 689 690
    if (!Core::ICore::mimeDatabase()->addMimeTypes(QLatin1String(RC_GIT_MIME_XML), errorMessage))
        return false;

Petar Perisin's avatar
Petar Perisin committed
691
    /* "Gerrit" */
692 693 694 695 696 697
    m_gerritPlugin = new Gerrit::Internal::GerritPlugin(this);
    const bool ok = m_gerritPlugin->initialize(remoteRepositoryMenu);
    m_gerritPlugin->updateActions(currentState().hasTopLevel());
    m_gerritPlugin->addToLocator(m_commandLocator);

    return ok;
con's avatar
con committed
698 699
}

700
GitVersionControl *GitPlugin::gitVersionControl() const
701
{
702
    return static_cast<GitVersionControl *>(versionControl());
703 704
}

705
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
706
{
707
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
708 709
}

710 711 712 713 714
void GitPlugin::submitEditorMerge(const QStringList &unmerged)
{
    m_gitClient->merge(m_submitRepository, unmerged);
}

con's avatar
con committed
715 716
void GitPlugin::diffCurrentFile()
{
hjk's avatar
hjk committed
717
    const VcsBase::VcsBasePluginState state = currentState();
718
    QTC_ASSERT(state.hasFile(), return);
719
    m_gitClient->diff(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile());
con's avatar
con committed
720 721 722 723
}

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
724
    const VcsBase::VcsBasePluginState state = currentState();
725
    QTC_ASSERT(state.hasProject(), return);
726
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
727 728
}

729 730
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
731
    const VcsBase::VcsBasePluginState state = currentState();
732
    QTC_ASSERT(state.hasTopLevel(), return);
733 734 735
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

con's avatar
con committed
736 737
void GitPlugin::logFile()
{
hjk's avatar
hjk committed
738
    const VcsBase::VcsBasePluginState state = currentState();
739
    QTC_ASSERT(state.hasFile(), return);
740
    m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
con's avatar
con committed
741 742 743 744
}

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
745
    const VcsBase::VcsBasePluginState state = currentState();
746
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
747
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
748
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
749 750 751 752
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
753
    const VcsBase::VcsBasePluginState state = currentState();
754
    QTC_ASSERT(state.hasProject(), return);
755
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
756 757
}

758 759 760 761 762 763 764
void GitPlugin::logRepository()
{
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    m_gitClient->log(state.topLevel());
}

765
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
766
{
hjk's avatar
hjk committed
767
    const VcsBase::VcsBasePluginState state = currentState();
768
    QTC_ASSERT(state.hasFile(), return);
769
    Core::FileChangeBlocker fcb(state.currentFile());
770 771 772 773 774 775
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

void GitPlugin::undoUnstagedFileChanges()
{
    undoFileChanges(false);
con's avatar
con committed
776 777
}

778
void GitPlugin::resetRepository()
con's avatar
con committed
779
{
hjk's avatar
hjk committed
780
    const VcsBase::VcsBasePluginState state = currentState();
781
    QTC_ASSERT(state.hasTopLevel(), return);
782
    QString topLevel = state.topLevel();
783

784
    LogChangeDialog dialog(true);
785 786
    dialog.setWindowTitle(tr("Undo Changes to %1").arg(QDir::toNativeSeparators(topLevel)));
    if (dialog.runDialog(topLevel))
Orgad Shaneh's avatar
Orgad Shaneh committed
787
        m_gitClient->reset(topLevel, dialog.resetFlag(), dialog.commit());
con's avatar
con committed
788 789
}

Orgad Shaneh's avatar
Orgad Shaneh committed
790 791
void GitPlugin::startRebase()
{
792 793 794 795
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    const QString topLevel = state.topLevel();
    if (topLevel.isEmpty() || !m_gitClient->canRebase(topLevel))
Orgad Shaneh's avatar
Orgad Shaneh committed
796
        return;
797
    if (!m_gitClient->beginStashScope(topLevel, QLatin1String("Rebase-i")))
Orgad Shaneh's avatar
Orgad Shaneh committed
798 799 800
        return;
    LogChangeDialog dialog(false);
    dialog.setWindowTitle(tr("Interactive Rebase"));
801 802
    if (dialog.runDialog(topLevel, QString(), false))
        m_gitClient->interactiveRebase(topLevel, dialog.commit(), false);
Orgad Shaneh's avatar
Orgad Shaneh committed
803
    else
804
        m_gitClient->endStashScope(topLevel);
Orgad Shaneh's avatar
Orgad Shaneh committed
805 806
}

807
void GitPlugin::startChangeRelatedAction()
808
{
Orgad Shaneh's avatar
Orgad Shaneh committed
809
    const VcsBase::VcsBasePluginState state = currentState();
810
    if (!state.hasTopLevel())
Orgad Shaneh's avatar
Orgad Shaneh committed
811
        return;
812

Orgad Shaneh's avatar
Orgad Shaneh committed
813
    ChangeSelectionDialog dialog(state.topLevel(), Core::ICore::mainWindow());
814

Orgad Shaneh's avatar
Orgad Shaneh committed
815
    int result = dialog.exec();
816

Orgad Shaneh's avatar
Orgad Shaneh committed
817
    if (result == QDialog::Rejected)
Orgad Shaneh's avatar
Orgad Shaneh committed
818
        return;
819

Orgad Shaneh's avatar
Orgad Shaneh committed
820 821
    const QString workingDirectory = dialog.workingDirectory();
    const QString change = dialog.change();
822

Petar Perisin's avatar