gitplugin.cpp 62.9 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>
62
#include <coreplugin/vcsmanager.h>
hjk's avatar
hjk committed
63
64

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

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

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

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

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

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

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

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

// GitPlugin

GitPlugin *GitPlugin::m_instance = 0;

GitPlugin::GitPlugin() :
Friedemann Kleint's avatar
Friedemann Kleint committed
132
    m_commandLocator(0),
con's avatar
con committed
133
134
135
136
    m_submitCurrentAction(0),
    m_diffSelectedFilesAction(0),
    m_undoAction(0),
    m_redoAction(0),
137
    m_menuAction(0),
138
    m_applyCurrentFilePatchAction(0),
con's avatar
con committed
139
    m_gitClient(0),
140
    m_submitActionTriggered(false)
con's avatar
con committed
141
142
{
    m_instance = this;
143
144
145
146
147
    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
148
149
150
151
}

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

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

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

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

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

183
184
// Create a parameter action
ParameterActionCommandPair
Eike Ziller's avatar
Eike Ziller committed
185
        GitPlugin::createParameterAction(Core::ActionContainer *ac,
186
                                         const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
187
                                         const Core::Id &id, const Core::Context &context,
188
189
190
                                         bool addToLocator)
{
    Utils::ParameterAction *action = new Utils::ParameterAction(defaultText, parameterText,
191
192
                                                                Utils::ParameterAction::EnabledWithParameter,
                                                                this);
Eike Ziller's avatar
Eike Ziller committed
193
    Core::Command *command = Core::ActionManager::registerAction(action, id, context);
194
195
196
197
198
199
200
201
202
    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
203
        GitPlugin::createFileAction(Core::ActionContainer *ac,
204
                                    const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
205
                                    const Core::Id &id, const Core::Context &context, bool addToLocator,
206
207
                                    const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
208
    const ParameterActionCommandPair rc = createParameterAction(ac, defaultText, parameterText, id, context, addToLocator);
209
210
211
212
213
214
215
    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
216
        GitPlugin::createProjectAction(Core::ActionContainer *ac,
217
                                       const QString &defaultText, const QString &parameterText,
hjk's avatar
hjk committed
218
                                       const Core::Id &id, const Core::Context &context, bool addToLocator,
219
220
                                       const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
221
    const ParameterActionCommandPair rc = createParameterAction(ac, defaultText, parameterText, id, context, addToLocator);
222
223
224
225
226
227
228
    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
229
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
230
                                          const QString &text, const Core::Id &id,
231
                                          const Core::Context &context, bool addToLocator)
232
233
{
    QAction  *action = new QAction(text, this);
Eike Ziller's avatar
Eike Ziller committed
234
    Core::Command *command = Core::ActionManager::registerAction(action, id, context);
235
236
    if (ac)
        ac->addAction(command);
237
238
239
240
241
242
243
244
    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
245
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
246
                                          const QString &text, const Core::Id &id,
247
                                          const Core::Context &context, bool addToLocator,
248
249
                                          const char *pluginSlot)
{
Eike Ziller's avatar
Eike Ziller committed
250
    const ActionCommandPair rc = createRepositoryAction(ac, text, id, context, addToLocator);
251
252
253
254
255
256
257
    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
258
        GitPlugin::createRepositoryAction(Core::ActionContainer *ac,
hjk's avatar
hjk committed
259
                                          const QString &text, const Core::Id &id,
260
                                          const Core::Context &context, bool addToLocator,
261
262
263
                                          GitClientMemberFunc func)
{
    // Set the member func as data and connect to generic slot
Eike Ziller's avatar
Eike Ziller committed
264
    const ActionCommandPair rc = createRepositoryAction(ac, text, id, context, addToLocator);
265
266
267
268
269
    rc.first->setData(qVariantFromValue(func));
    connect(rc.first, SIGNAL(triggered()), this, SLOT(gitClientMemberFuncRepositoryAction()));
    return rc;
}

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

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

    m_gitClient = new GitClient(&m_settings);
277

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

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

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

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

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

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

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

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

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

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

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

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

Petar Perisin's avatar
Petar Perisin committed
329
330
331
332
333
334
335
    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")));

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

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

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

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

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

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

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

    /*  "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
371

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

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

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

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

    // --------------
con's avatar
con committed
396

Petar Perisin's avatar
Petar Perisin committed
397
398
399
400
401
402
    /*  "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
403
                           tr("Diff"), Core::Id("Git.DiffRepository"),
404
                           globalcontext, true, SLOT(diffRepository()));
405

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

Orgad Shaneh's avatar
Orgad Shaneh committed
411
412
413
414
415
    createRepositoryAction(localRepositoryMenu,
                           tr("Reflog"), Core::Id("Git.ReflogRepository"),
                           globalcontext, true,
                           SLOT(reflogRepository()));

Petar Perisin's avatar
Petar Perisin committed
416
417
418
419
420
    createRepositoryAction(localRepositoryMenu,
                           tr("Clean..."), Core::Id("Git.CleanRepository"),
                           globalcontext, true, SLOT(cleanRepository()));

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

Petar Perisin's avatar
Petar Perisin committed
424
425
    // --------------
    localRepositoryMenu->addSeparator(globalcontext);
con's avatar
con committed
426

Petar Perisin's avatar
Petar Perisin committed
427
428
429
430
    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")));
431

Petar Perisin's avatar
Petar Perisin committed
432
433
434
    createRepositoryAction(localRepositoryMenu,
                           tr("Amend Last Commit..."), Core::Id("Git.AmendCommit"),
                           globalcontext, true, SLOT(startAmendCommit()));
435

436
437
438
439
    m_fixupCommitAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Fixup Previous Commit..."), Core::Id("Git.FixupCommit"),
                                   globalcontext, true, SLOT(startFixupCommit())).first;
440
441
    // --------------
    localRepositoryMenu->addSeparator(globalcontext);
442

Petar Perisin's avatar
Petar Perisin committed
443
444
    createRepositoryAction(localRepositoryMenu,
                           tr("Reset..."), Core::Id("Git.Reset"),
Orgad Shaneh's avatar
Orgad Shaneh committed
445
                           globalcontext, true, SLOT(resetRepository()));
446

447
448
449
450
    m_interactiveRebaseAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Interactive Rebase..."), Core::Id("Git.InteractiveRebase"),
                                   globalcontext, true, SLOT(startRebase())).first;
Orgad Shaneh's avatar
Orgad Shaneh committed
451

Petar Perisin's avatar
Petar Perisin committed
452
453
454
455
    m_submoduleUpdateAction =
            createRepositoryAction(localRepositoryMenu,
                                   tr("Update Submodules"), Core::Id("Git.SubmoduleUpdate"),
                                   globalcontext, true, SLOT(updateSubmodules())).first;
Petar Perisin's avatar
Petar Perisin committed
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
482
483
484
485
486
487
488
489
    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
490

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

Petar Perisin's avatar
Petar Perisin committed
494
    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
495
                           tr("Branches..."), Core::Id("Git.BranchList"),
Yuchen Deng's avatar
Yuchen Deng committed
496
                           globalcontext, true, SLOT(branchList()));
497
498

    // --------------
Petar Perisin's avatar
Petar Perisin committed
499
    localRepositoryMenu->addSeparator(globalcontext);
500

Petar Perisin's avatar
Petar Perisin committed
501
    // "Patch" menu
Eike Ziller's avatar
Eike Ziller committed
502
    Core::ActionContainer *patchMenu = Core::ActionManager::createMenu(Core::Id("Git.PatchMenu"));
Petar Perisin's avatar
Petar Perisin committed
503
504
    patchMenu->menu()->setTitle(tr("&Patch"));
    localRepositoryMenu->addMenu(patchMenu);
505
506
507

    // Apply current file as patch is handled specially.
    parameterActionCommand =
Eike Ziller's avatar
Eike Ziller committed
508
            createParameterAction(patchMenu,
509
                                  tr("Apply from Editor"), tr("Apply \"%1\""),
hjk's avatar
hjk committed
510
                                  Core::Id("Git.ApplyCurrentFilePatch"),
511
512
513
514
515
                                  globalcontext, true);
    m_applyCurrentFilePatchAction = parameterActionCommand.first;
    connect(m_applyCurrentFilePatchAction, SIGNAL(triggered()), this,
            SLOT(applyCurrentFilePatch()));

Eike Ziller's avatar
Eike Ziller committed
516
    createRepositoryAction(patchMenu,
hjk's avatar
hjk committed
517
                           tr("Apply from File..."), Core::Id("Git.ApplyPatch"),
518
519
                           globalcontext, true, SLOT(promptApplyPatch()));

Petar Perisin's avatar
Petar Perisin committed
520
    // "Stash" menu
Eike Ziller's avatar
Eike Ziller committed
521
    Core::ActionContainer *stashMenu = Core::ActionManager::createMenu(Core::Id("Git.StashMenu"));
Petar Perisin's avatar
Petar Perisin committed
522
523
    stashMenu->menu()->setTitle(tr("&Stash"));
    localRepositoryMenu->addMenu(stashMenu);
524

Eike Ziller's avatar
Eike Ziller committed
525
    createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
526
                           tr("Stashes..."), Core::Id("Git.StashList"),
527
528
                           globalcontext, false, SLOT(stashList()));

529
    stashMenu->addSeparator(globalcontext);
530

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

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

541
    stashMenu->addSeparator(globalcontext);
542

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

Petar Perisin's avatar
Petar Perisin committed
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573

    /* \"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
574
    Core::ActionContainer *subversionMenu = Core::ActionManager::createMenu(Core::Id("Git.Subversion"));
Petar Perisin's avatar
Petar Perisin committed
575
576
    subversionMenu->menu()->setTitle(tr("&Subversion"));
    remoteRepositoryMenu->addMenu(subversionMenu);
577

Eike Ziller's avatar
Eike Ziller committed
578
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
579
                           tr("Log"), Core::Id("Git.Subversion.Log"),
580
581
                           globalcontext, false, &GitClient::subversionLog);

Eike Ziller's avatar
Eike Ziller committed
582
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
583
                           tr("Fetch"), Core::Id("Git.Subversion.Fetch"),
584
585
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

Petar Perisin's avatar
Petar Perisin committed
586
587
    // --------------
    remoteRepositoryMenu->addSeparator(globalcontext);
588

Petar Perisin's avatar
Petar Perisin committed
589
590
591
    createRepositoryAction(remoteRepositoryMenu,
                           tr("Manage Remotes..."), Core::Id("Git.RemoteList"),
                           globalcontext, false, SLOT(remoteList()));
592

Petar Perisin's avatar
Petar Perisin committed
593
    /* \"Remote Repository" menu */
Robert Loehning's avatar
Robert Loehning committed
594

Petar Perisin's avatar
Petar Perisin committed
595
    // --------------
596

597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
    /*  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
620
621
622
623
    /*  "Git Tools" menu */
    Core::ActionContainer *gitToolsMenu = Core::ActionManager::createMenu(Core::Id("Git.GitToolsMenu"));
    gitToolsMenu->menu()->setTitle(tr("Git &Tools"));
    gitContainer->addMenu(gitToolsMenu);
624

Petar Perisin's avatar
Petar Perisin committed
625
626
627
    createRepositoryAction(gitToolsMenu,
                           tr("Gitk"), Core::Id("Git.LaunchGitK"),
                           globalcontext, true, &GitClient::launchGitK);
628

Petar Perisin's avatar
Petar Perisin committed
629
630
631
632
633
634
635
636
637
638
639
640
641
    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);

642
643
644
645
646
647
    createRepositoryAction(gitToolsMenu, tr("Git Gui"), Core::Id("Git.GitGui"),
                           globalcontext, true, SLOT(gitGui()));

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

Petar Perisin's avatar
Petar Perisin committed
648
649
650
651
    m_repositoryBrowserAction
            = createRepositoryAction(gitToolsMenu,
                                     tr("Repository Browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;
652

Petar Perisin's avatar
Petar Perisin committed
653
654
655
656
    m_mergeToolAction =
            createRepositoryAction(gitToolsMenu,
                                   tr("Merge Tool"), Core::Id("Git.MergeTool"),
                                   globalcontext, true, SLOT(startMergeTool())).first;
657

Petar Perisin's avatar
Petar Perisin committed
658
659
660
    /* \"Git Tools" menu */

    // --------------
661
    gitContainer->addSeparator(globalcontext);
662

663
664
665
666
667
    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);

668
669
670
    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
671
672
    gitContainer->addAction(createRepositoryCommand);

673
674
675
676
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
677
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
678
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
679
                                                    Core::Id("Git.Snapshot.").withSuffix(i),
680
                                                    globalcontext);
681
            gitContainer->addAction(tCommand);
682
683
684
        }
    }

con's avatar
con committed
685
    // Submit editor
686
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
687
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
688
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
689
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
690
691
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

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

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

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

701
702
    connect(Core::ICore::vcsManager(), SIGNAL(repositoryChanged(QString)),
            this, SLOT(updateContinueAndAbortCommands()));
703
    connect(Core::ICore::vcsManager(), SIGNAL(repositoryChanged(QString)),
704
            this, SLOT(updateBranches(QString)), Qt::QueuedConnection);
705

706
707
708
    if (!Core::ICore::mimeDatabase()->addMimeTypes(QLatin1String(RC_GIT_MIME_XML), errorMessage))
        return false;

Petar Perisin's avatar
Petar Perisin committed
709
    /* "Gerrit" */
710
711
712
713
714
715
    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
716
717
}

718
GitVersionControl *GitPlugin::gitVersionControl() const
719
{
720
    return static_cast<GitVersionControl *>(versionControl());
721
722
}

723
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
724
{
Orgad Shaneh's avatar
Orgad Shaneh committed
725
    m_gitClient->diff(m_submitRepository, unstaged, staged);
con's avatar
con committed
726
727
}

728
729
730
731
732
void GitPlugin::submitEditorMerge(const QStringList &unmerged)
{
    m_gitClient->merge(m_submitRepository, unmerged);
}

733
734
735
736
737
738
739
740
static bool ensureAllDocumentsSaved()
{
    bool cancelled;
    Core::DocumentManager::saveModifiedDocuments(Core::DocumentManager::modifiedDocuments(),
                                                 &cancelled);
    return !cancelled;
}

con's avatar
con committed
741
742
void GitPlugin::diffCurrentFile()
{
hjk's avatar
hjk committed
743
    const VcsBase::VcsBasePluginState state = currentState();
744
    QTC_ASSERT(state.hasFile(), return);
Orgad Shaneh's avatar
Orgad Shaneh committed
745
    m_gitClient->diff(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
746
747
748
749
}

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
750
    const VcsBase::VcsBasePluginState state = currentState();
751
    QTC_ASSERT(state.hasProject(), return);
752
753
754
    const QString relativeProject = state.relativeCurrentProject();
    m_gitClient->diff(state.currentProjectTopLevel(),
                      relativeProject.isEmpty() ? QStringList() : QStringList(relativeProject));
con's avatar
con committed
755
756
}

757
758
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
759
    const VcsBase::VcsBasePluginState state = currentState();
760
    QTC_ASSERT(state.hasTopLevel(), return);
Orgad Shaneh's avatar
Orgad Shaneh committed
761
    m_gitClient->diff(state.topLevel(), QStringList());
762
763
}

con's avatar
con committed
764
765
void GitPlugin::logFile()
{
hjk's avatar
hjk committed
766
    const VcsBase::VcsBasePluginState state = currentState();
767
    QTC_ASSERT(state.hasFile(), return);
768
    m_gitClient->log(state.currentFileTopLevel(), state.relativeCurrentFile(), true);
con's avatar
con committed
769
770
771
772
}

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
773
    const VcsBase::VcsBasePluginState state = currentState();
774
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
775
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
776
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
777
778
779
780
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
781
    const VcsBase::VcsBasePluginState state = currentState();
782
    QTC_ASSERT(state.hasProject(), return);
783
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
784
785
}

786
787
788
789
790
791
792
void GitPlugin::logRepository()
{
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    m_gitClient->log(state.topLevel());
}

Orgad Shaneh's avatar
Orgad Shaneh committed
793
794
795
796
797
798
799
void GitPlugin::reflogRepository()
{
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    m_gitClient->reflog(state.topLevel());
}

800
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
801
{
802
803
    if (!ensureAllDocumentsSaved())
        return;
hjk's avatar
hjk committed
804
    const VcsBase::VcsBasePluginState state = currentState();
805
    QTC_ASSERT(state.hasFile(), return);
806
    Core::FileChangeBlocker fcb(state.currentFile());
807
808
809
810
811
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

void GitPlugin::undoUnstagedFileChanges()
{
812
813
    if (!ensureAllDocumentsSaved())
        return;
814
    undoFileChanges(false);
con's avatar
con committed
815
816
}

817
void GitPlugin::resetRepository()
con's avatar
con committed
818
{
819
820
    if (!ensureAllDocumentsSaved())
        return;
hjk's avatar
hjk committed
821
    const VcsBase::VcsBasePluginState state = currentState();
822
    QTC_ASSERT(state.hasTopLevel(), return);
823
    QString topLevel = state.topLevel();
824

825
    LogChangeDialog dialog(true);
826
827
    dialog.setWindowTitle(tr("Undo Changes to %1").arg(QDir::toNativeSeparators(topLevel)));
    if (dialog.runDialog(topLevel))
Orgad Shaneh's avatar
Orgad Shaneh committed
828
        m_gitClient->reset(topLevel, dialog.resetFlag(), dialog.commit());
con's avatar
con committed
829
830
}

Orgad Shaneh's avatar
Orgad Shaneh committed
831
832
void GitPlugin::startRebase()
{
833
834
    if (!ensureAllDocumentsSaved())
        return;
835
836
837
838
    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
839
        return;
840
    if (!m_gitClient->beginStashScope(topLevel, QLatin1String("Rebase-i")))
Orgad Shaneh's avatar
Orgad Shaneh committed
841
842
843
        return;
    LogChangeDialog dialog(false);
    dialog.setWindowTitle(tr("Interactive Rebase"));
844
845
    if (dialog.runDialog(topLevel, QString(), false))
        m_gitClient->interactiveRebase(topLevel, dialog.commit(), false);
Orgad Shaneh's avatar
Orgad Shaneh committed
846
    else
847
        m_gitClient->endStashScope(topLevel);
Orgad Shaneh's avatar
Orgad Shaneh committed
848
849
}

850
void GitPlugin::startChangeRelatedAction()
851
{
Orgad Shaneh's avatar
Orgad Shaneh committed
852
    const VcsBase::VcsBasePluginState state = currentState();
853
    if (!state.hasTopLevel())
Orgad Shaneh's avatar
Orgad Shaneh committed
854
        return;
855

Orgad Shaneh's avatar <