gitplugin.cpp 42.1 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
con's avatar
con committed
5
** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
hjk's avatar
hjk committed
7
** Contact: Nokia Corporation (info@qt.nokia.com)
con's avatar
con committed
8
**
9
**
10
** GNU Lesser General Public License Usage
11
**
hjk's avatar
hjk committed
12
13
14
15
16
17
** 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.
18
**
con's avatar
con committed
19
** In addition, as a special exception, Nokia gives you certain additional
hjk's avatar
hjk committed
20
** rights. These rights are described in the Nokia Qt LGPL Exception
con's avatar
con committed
21
22
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
**
hjk's avatar
hjk committed
23
24
25
26
27
** Other Usage
**
** Alternatively, this file may be used in accordance with the terms and
** conditions contained in a signed written agreement between you and Nokia.
**
con's avatar
con committed
28
** If you have questions regarding the use of this file, please contact
Tobias Hunger's avatar
Tobias Hunger committed
29
** Nokia at info@qt.nokia.com.
con's avatar
con committed
30
**
31
**************************************************************************/
hjk's avatar
hjk committed
32

con's avatar
con committed
33
#include "gitplugin.h"
hjk's avatar
hjk committed
34
35
36

#include "changeselectiondialog.h"
#include "commitdata.h"
con's avatar
con committed
37
38
#include "gitclient.h"
#include "gitconstants.h"
hjk's avatar
hjk committed
39
#include "giteditor.h"
con's avatar
con committed
40
#include "gitsubmiteditor.h"
hjk's avatar
hjk committed
41
#include "gitversioncontrol.h"
42
#include "branchdialog.h"
43
#include "remotedialog.h"
44
#include "clonewizard.h"
45
#include "gitoriousclonewizard.h"
46
#include "stashdialog.h"
47
#include "settingspage.h"
con's avatar
con committed
48
49
50
51
52

#include <coreplugin/icore.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/filemanager.h>
#include <coreplugin/messagemanager.h>
53
#include <coreplugin/actionmanager/actionmanager.h>
54
55
#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/command.h>
56
#include <coreplugin/id.h>
con's avatar
con committed
57
#include <coreplugin/editormanager/editormanager.h>
58
59
#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/filemanager.h>
hjk's avatar
hjk committed
60
61

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

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

#include <QtCore/QDebug>
hjk's avatar
hjk committed
73
#include <QtCore/QDir>
con's avatar
con committed
74
75
#include <QtCore/QFileInfo>
#include <QtCore/QTemporaryFile>
76
#include <QtCore/QtPlugin>
hjk's avatar
hjk committed
77

con's avatar
con committed
78
#include <QtGui/QAction>
hjk's avatar
hjk committed
79
80
#include <QtGui/QFileDialog>
#include <QtGui/QMainWindow>
con's avatar
con committed
81
82
83
84
85
86
#include <QtGui/QMenu>
#include <QtGui/QMessageBox>

static const VCSBase::VCSBaseEditorParameters editorParameters[] = {
{
    VCSBase::RegularCommandOutput,
87
88
    Git::Constants::GIT_COMMAND_LOG_EDITOR_ID,
    Git::Constants::GIT_COMMAND_LOG_EDITOR_DISPLAY_NAME,
89
    Git::Constants::C_GIT_COMMAND_LOG_EDITOR,
con's avatar
con committed
90
91
92
    "application/vnd.nokia.text.scs_git_commandlog",
    "gitlog"},
{   VCSBase::LogOutput,
93
94
    Git::Constants::GIT_LOG_EDITOR_ID,
    Git::Constants::GIT_LOG_EDITOR_DISPLAY_NAME,
95
    Git::Constants::C_GIT_LOG_EDITOR,
con's avatar
con committed
96
97
98
    "application/vnd.nokia.text.scs_git_filelog",
    "gitfilelog"},
{   VCSBase::AnnotateOutput,
99
100
    Git::Constants::GIT_BLAME_EDITOR_ID,
    Git::Constants::GIT_BLAME_EDITOR_DISPLAY_NAME,
101
    Git::Constants::C_GIT_BLAME_EDITOR,
con's avatar
con committed
102
103
104
    "application/vnd.nokia.text.scs_git_annotation",
    "gitsannotate"},
{   VCSBase::DiffOutput,
105
106
    Git::Constants::GIT_DIFF_EDITOR_ID,
    Git::Constants::GIT_DIFF_EDITOR_DISPLAY_NAME,
107
    Git::Constants::C_GIT_DIFF_EDITOR,
con's avatar
con committed
108
109
110
111
112
113
114
    "text/x-patch","diff"}
};

// Utility to find a parameter set by type
static inline const VCSBase::VCSBaseEditorParameters *findType(int ie)
{
    const VCSBase::EditorContentType et = static_cast<VCSBase::EditorContentType>(ie);
115
    return  VCSBase::VCSBaseEditorWidget::findType(editorParameters, sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters), et);
con's avatar
con committed
116
117
}

118
119
Q_DECLARE_METATYPE(Git::Internal::GitClientMemberFunc)

con's avatar
con committed
120
121
122
123
124
125
126
127
using namespace Git;
using namespace Git::Internal;

// GitPlugin

GitPlugin *GitPlugin::m_instance = 0;

GitPlugin::GitPlugin() :
128
    VCSBase::VCSBasePlugin(QLatin1String(Git::Constants::GITSUBMITEDITOR_ID)),
con's avatar
con committed
129
    m_core(0),
Friedemann Kleint's avatar
Friedemann Kleint committed
130
    m_commandLocator(0),
con's avatar
con committed
131
132
133
134
135
    m_showAction(0),
    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
139
    m_gitClient(0),
    m_changeSelectionDialog(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
175
176
GitPlugin *GitPlugin::instance()
{
    return m_instance;
}

static const VCSBase::VCSBaseSubmitEditorParameters submitParameters = {
    Git::Constants::SUBMIT_MIMETYPE,
177
178
    Git::Constants::GITSUBMITEDITOR_ID,
    Git::Constants::GITSUBMITEDITOR_DISPLAY_NAME,
179
    Git::Constants::C_GITSUBMITEDITOR
con's avatar
con committed
180
181
};

con's avatar
con committed
182
static Core::Command *createSeparator(Core::ActionManager *am,
183
                                       const Core::Context &context,
hjk's avatar
hjk committed
184
                                       const Core::Id &id,
hjk's avatar
hjk committed
185
                                       QObject *parent)
186
187
188
{
    QAction *a = new QAction(parent);
    a->setSeparator(true);
189
    return am->registerAction(a, id, context);
190
}
con's avatar
con committed
191

192
193
194
195
// Create a parameter action
ParameterActionCommandPair
        GitPlugin::createParameterAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                         const QString &defaultText, const QString &parameterText,
196
                                         const QString &id, const Core::Context &context,
197
198
199
                                         bool addToLocator)
{
    Utils::ParameterAction *action = new Utils::ParameterAction(defaultText, parameterText,
200
201
                                                                Utils::ParameterAction::EnabledWithParameter,
                                                                this);
202
203
204
205
206
207
208
209
210
211
212
213
    Core::Command *command = am->registerAction(action, id, context);
    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
        GitPlugin::createFileAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                    const QString &defaultText, const QString &parameterText,
214
                                    const QString &id, const Core::Context &context, bool addToLocator,
215
216
217
218
219
220
221
222
223
224
225
226
                                    const char *pluginSlot)
{
    const ParameterActionCommandPair rc = createParameterAction(am, ac, defaultText, parameterText, id, context, addToLocator);
    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
        GitPlugin::createProjectAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                       const QString &defaultText, const QString &parameterText,
227
                                       const QString &id, const Core::Context &context, bool addToLocator,
228
229
230
231
232
233
234
235
236
237
238
239
                                       const char *pluginSlot)
{
    const ParameterActionCommandPair rc = createParameterAction(am, ac, defaultText, parameterText, id, context, addToLocator);
    m_projectActions.push_back(rc.first);
    connect(rc.first, SIGNAL(triggered()), this, pluginSlot);
    return rc;
}

// Create an action to act on the repository
ActionCommandPair
        GitPlugin::createRepositoryAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                          const QString &text, const QString &id,
240
                                          const Core::Context &context, bool addToLocator)
241
242
243
244
245
246
247
248
249
250
251
252
253
254
{
    QAction  *action = new QAction(text, this);
    Core::Command *command = am->registerAction(action, id, context);
    ac->addAction(command);
    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
        GitPlugin::createRepositoryAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                          const QString &text, const QString &id,
255
                                          const Core::Context &context, bool addToLocator,
256
257
258
259
260
261
262
263
264
265
266
267
                                          const char *pluginSlot)
{
    const ActionCommandPair rc = createRepositoryAction(am, ac, text, id, context, addToLocator);
    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
        GitPlugin::createRepositoryAction(Core::ActionManager *am, Core::ActionContainer *ac,
                                          const QString &text, const QString &id,
268
                                          const Core::Context &context, bool addToLocator,
269
270
271
272
273
274
275
276
277
                                          GitClientMemberFunc func)
{
    // Set the member func as data and connect to generic slot
    const ActionCommandPair rc = createRepositoryAction(am, ac, text, id, context, addToLocator);
    rc.first->setData(qVariantFromValue(func));
    connect(rc.first, SIGNAL(triggered()), this, SLOT(gitClientMemberFuncRepositoryAction()));
    return rc;
}

278
bool GitPlugin::initialize(const QStringList &arguments, QString *errorMessage)
con's avatar
con committed
279
{
280
281
282
    Q_UNUSED(arguments)
    Q_UNUSED(errorMessage)

283
    m_core = Core::ICore::instance();
284
285
286
    m_settings.readSettings(m_core->settings());

    m_gitClient = new GitClient(&m_settings);
287

con's avatar
con committed
288
289
290
    typedef VCSBase::VCSEditorFactory<GitEditor> GitEditorFactory;
    typedef VCSBase::VCSSubmitEditorFactory<GitSubmitEditor> GitSubmitEditorFactory;

291
    initializeVcs(new GitVersionControl(m_gitClient));
con's avatar
con committed
292

293
    // Create the globalcontext list to register actions accordingly
294
    Core::Context globalcontext(Core::Constants::C_GLOBAL);
con's avatar
con committed
295
296

    // Create the settings Page
297
    addAutoReleasedObject(new SettingsPage());
con's avatar
con committed
298
299
300

    static const char *describeSlot = SLOT(show(QString,QString));
    const int editorCount = sizeof(editorParameters)/sizeof(VCSBase::VCSBaseEditorParameters);
301
302
    for (int i = 0; i < editorCount; i++)
        addAutoReleasedObject(new GitEditorFactory(editorParameters + i, m_gitClient, describeSlot));
con's avatar
con committed
303

304
305
    addAutoReleasedObject(new GitSubmitEditorFactory(&submitParameters));
    addAutoReleasedObject(new CloneWizard);
306
    addAutoReleasedObject(new Gitorious::Internal::GitoriousCloneWizard);
307

Friedemann Kleint's avatar
Friedemann Kleint committed
308
309
310
311
312
    const QString description = QLatin1String("Git");
    const QString prefix = QLatin1String("git");
    m_commandLocator = new Locator::CommandLocator(description, prefix, prefix);
    addAutoReleasedObject(m_commandLocator);

con's avatar
con committed
313
    //register actions
314
    Core::ActionManager *actionManager = m_core->actionManager();
con's avatar
con committed
315

316
    Core::ActionContainer *toolsContainer =
con's avatar
con committed
317
318
        actionManager->actionContainer(Core::Constants::M_TOOLS);

319
    Core::ActionContainer *gitContainer =
Friedemann Kleint's avatar
Friedemann Kleint committed
320
        actionManager->createMenu(description);
con's avatar
con committed
321
322
    gitContainer->menu()->setTitle(tr("&Git"));
    toolsContainer->addMenu(gitContainer);
323
    m_menuAction = gitContainer->menu()->menuAction();
324

325
326
    ParameterActionCommandPair parameterActionCommand
            = createFileAction(actionManager, gitContainer,
Tobias Hunger's avatar
Tobias Hunger committed
327
                               tr("Blame Current File"), tr("Blame for \"%1\""),
328
329
330
331
                               QLatin1String("Git.Blame"),
                               globalcontext, true, SLOT(blameFile()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+B")));

332
333
    parameterActionCommand
            = createFileAction(actionManager, gitContainer,
334
335
336
337
                               tr("Diff Current File"), tr("Diff of \"%1\""),
                               QLatin1String("Git.Diff"), globalcontext, true,
                               SLOT(diffCurrentFile()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+D")));
338

339
    parameterActionCommand
340
341
342
343
344
345
346
347
            = createFileAction(actionManager, gitContainer,
                               tr("Log Current File"), tr("Log of \"%1\""),
                               QLatin1String("Git.Log"), globalcontext, true, SLOT(logFile()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+L")));


    // ------
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.File"), this));
348
349
350
351
352
353
354
355
356
357
358

    parameterActionCommand
            = createFileAction(actionManager, gitContainer,
                               tr("Stage File for Commit"), tr("Stage \"%1\" for Commit"),
                               QLatin1String("Git.Stage"), globalcontext, true, SLOT(stageFile()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+A")));

    parameterActionCommand
            = createFileAction(actionManager, gitContainer,
                               tr("Unstage File from Commit"), tr("Unstage \"%1\" from Commit"),
                               QLatin1String("Git.Unstage"), globalcontext, true, SLOT(unstageFile()));
359

360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
    parameterActionCommand
            = createFileAction(actionManager, gitContainer,
                               tr("Undo Unstaged Changes"), tr("Undo Unstaged Changes for \"%1\""),
                               QLatin1String("Git.UndoUnstaged"), globalcontext,
                               true, SLOT(undoUnstagedFileChanges()));

    parameterActionCommand
            = createFileAction(actionManager, gitContainer,
                               tr("Undo Uncommitted Changes"), tr("Undo Uncommitted Changes for \"%1\""),
                               QLatin1String("Git.Undo"), globalcontext,
                               true, SLOT(undoFileChanges()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+U")));


    // ------------
hjk's avatar
hjk committed
375
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.Project"), this));
con's avatar
con committed
376

377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
    parameterActionCommand
            = createProjectAction(actionManager, gitContainer,
                                  tr("Diff Current Project"), tr("Diff Project \"%1\""),
                                  QLatin1String("Git.DiffProject"),
                                  globalcontext, true,
                                  SLOT(diffCurrentProject()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence("Alt+G,Alt+Shift+D"));

    parameterActionCommand
            = createProjectAction(actionManager, gitContainer,
                                  tr("Log Project"), tr("Log Project \"%1\""),
                                  QLatin1String("Git.LogProject"), globalcontext, true,
                                  SLOT(logProject()));
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+K")));

    parameterActionCommand
                = createProjectAction(actionManager, gitContainer,
                                      tr("Clean Project..."), tr("Clean Project \"%1\"..."),
                                      QLatin1String("Git.CleanProject"), globalcontext,
                                      true, SLOT(cleanProject()));
con's avatar
con committed
397

398
399

    // --------------
hjk's avatar
hjk committed
400
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.Repository"), this));
con's avatar
con committed
401

402
    createRepositoryAction(actionManager, gitContainer,
403
                           tr("Diff"), QLatin1String("Git.DiffRepository"),
404
                           globalcontext, true, SLOT(diffRepository()));
405

406
    createRepositoryAction(actionManager, gitContainer,
407
                           tr("Log"), QLatin1String("Git.LogRepository"),
408
                           globalcontext, true, &GitClient::graphLog);
409

410
    createRepositoryAction(actionManager, gitContainer,
411
412
                           tr("Status"), QLatin1String("Git.StatusRepository"),
                           globalcontext, true, &GitClient::status);
413

414
    createRepositoryAction(actionManager, gitContainer,
415
                           tr("Undo Uncommited Changes..."), QLatin1String("Git.UndoRepository"),
416
                           globalcontext, false, SLOT(undoRepositoryChanges()));
con's avatar
con committed
417

418
419
420
421
422

    createRepositoryAction(actionManager, gitContainer,
                           tr("Clean..."), QLatin1String("Git.CleanRepository"),
                           globalcontext, true, SLOT(cleanRepository()));

423
    m_createRepositoryAction = new QAction(tr("Create Repository..."), this);
424
    Core::Command *createRepositoryCommand = actionManager->registerAction(m_createRepositoryAction, "Git.CreateRepository", globalcontext);
425
    connect(m_createRepositoryAction, SIGNAL(triggered()), this, SLOT(createRepository()));
426
    gitContainer->addAction(createRepositoryCommand);
427

428
429
    // --------------
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.Info"), this));
430

431
432
    createRepositoryAction(actionManager, gitContainer,
                           tr("Launch gitk"), QLatin1String("Git.LaunchGitK"),
433
                           globalcontext, true, &GitClient::launchGitK);
434

435
436
437
438
    createRepositoryAction(actionManager, gitContainer,
                           tr("Branches..."), QLatin1String("Git.BranchList"),
                           globalcontext, false, SLOT(branchList()));

439
440
441
442
    createRepositoryAction(actionManager, gitContainer,
                           tr("Remotes..."), QLatin1String("Git.RemoteList"),
                           globalcontext, false, SLOT(remoteList()));

443
444
445
446
447
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
    m_showAction = new QAction(tr("Show Commit..."), this);
    Core::Command *showCommitCommand = actionManager->registerAction(m_showAction, "Git.ShowCommit", globalcontext);
    connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
    gitContainer->addAction(showCommitCommand);


    // --------------
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.RarelyUsed"), this));

    Core::ActionContainer *patchMenu = actionManager->createMenu(Core::Id("Git.PatchMenu"));
    patchMenu->menu()->setTitle(tr("Patch"));
    gitContainer->addMenu(patchMenu);

    // Apply current file as patch is handled specially.
    parameterActionCommand =
            createParameterAction(actionManager, patchMenu,
                                  tr("Apply from Editor"), tr("Apply \"%1\""),
                                  QLatin1String("Git.ApplyCurrentFilePatch"),
                                  globalcontext, true);
    m_applyCurrentFilePatchAction = parameterActionCommand.first;
    connect(m_applyCurrentFilePatchAction, SIGNAL(triggered()), this,
            SLOT(applyCurrentFilePatch()));

    createRepositoryAction(actionManager, patchMenu,
                           tr("Apply from File..."), QLatin1String("Git.ApplyPatch"),
                           globalcontext, true, SLOT(promptApplyPatch()));

    Core::ActionContainer *stashMenu = actionManager->createMenu(Core::Id("Git.StashMenu"));
    stashMenu->menu()->setTitle(tr("Stash"));
    gitContainer->addMenu(stashMenu);

    createRepositoryAction(actionManager, stashMenu,
                           tr("Stashes..."), QLatin1String("Git.StashList"),
                           globalcontext, false, SLOT(stashList()));

    stashMenu->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.StashMenuPush"), this));
479

480
    ActionCommandPair actionCommand =
481
482
483
484
485
486
487
            createRepositoryAction(actionManager, stashMenu,
                                   tr("Stash"), QLatin1String("Git.Stash"),
                                   globalcontext, true, SLOT(stash()));
    actionCommand.first->setToolTip(tr("Saves the current state of your work and resets the repository."));

    actionCommand = createRepositoryAction(actionManager, stashMenu,
                                           tr("Take Snapshot..."), QLatin1String("Git.StashSnapshot"),
488
489
490
                                           globalcontext, true, SLOT(stashSnapshot()));
    actionCommand.first->setToolTip(tr("Saves the current state of your work."));

491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
    stashMenu->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.StashMenuPop"), this));

    actionCommand = createRepositoryAction(actionManager, stashMenu,
                                           tr("Stash Pop"), QLatin1String("Git.StashPop"),
                                           globalcontext, true, &GitClient::stashPop);
    actionCommand.first->setToolTip(tr("Restores changes saved to the stash list using \"Stash\"."));

    Core::ActionContainer *subversionMenu = actionManager->createMenu(Core::Id("Git.Subversion"));
    subversionMenu->menu()->setTitle(tr("Subversion"));
    gitContainer->addMenu(subversionMenu);

    createRepositoryAction(actionManager, subversionMenu,
                           tr("Log"), QLatin1String("Git.Subversion.Log"),
                           globalcontext, false, &GitClient::subversionLog);

    createRepositoryAction(actionManager, subversionMenu,
                           tr("Fetch"), QLatin1String("Git.Subversion.Fetch"),
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.PushPull"), this));

    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.Global"), this));
513

Robert Loehning's avatar
Robert Loehning committed
514
515
516
517
    createRepositoryAction(actionManager, gitContainer,
                           tr("Fetch"), QLatin1String("Git.Fetch"),
                           globalcontext, true, SLOT(fetch()));

518
519
520
521
522
    createRepositoryAction(actionManager, gitContainer,
                           tr("Pull"), QLatin1String("Git.Pull"),
                           globalcontext, true, SLOT(pull()));

    actionCommand = createRepositoryAction(actionManager, gitContainer,
523
524
                                           tr("Push"), QLatin1String("Git.Push"),
                                           globalcontext, true, SLOT(push()));
525
526
527
528
529
530

    actionCommand = createRepositoryAction(actionManager, gitContainer,
                                           tr("Commit..."), QLatin1String("Git.Commit"),
                                           globalcontext, true, SLOT(startCommit()));
    actionCommand.second->setDefaultKeySequence(QKeySequence(tr("Alt+G,Alt+C")));

531
532
533
534
    createRepositoryAction(actionManager, gitContainer,
                           tr("Amend Last Commit..."), QLatin1String("Git.AmendCommit"),
                           globalcontext, true, SLOT(startAmendCommit()));

535
    // Subversion in a submenu.
hjk's avatar
hjk committed
536
    gitContainer->addAction(createSeparator(actionManager, globalcontext, Core::Id("Git.Sep.Subversion"), this));
537

538
539
540
541
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
542
543
            Core::Command *tCommand
                    = actionManager->registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
544
                                                    QString(QLatin1String("Git.Snapshot.") + QString::number(i)),
545
                                                    globalcontext);
546
            gitContainer->addAction(tCommand);
547
548
549
        }
    }

con's avatar
con committed
550
    // Submit editor
551
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
552
    m_submitCurrentAction = new QAction(VCSBase::VCSBaseSubmitEditor::submitIcon(), tr("Commit"), this);
553
    Core::Command *command = actionManager->registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
554
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
555
556
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

557
    m_diffSelectedFilesAction = new QAction(VCSBase::VCSBaseSubmitEditor::diffIcon(), tr("Diff &Selected Files"), this);
con's avatar
con committed
558
559
560
561
562
563
564
565
566
567
568
    command = actionManager->registerAction(m_diffSelectedFilesAction, Constants::DIFF_SELECTED, submitContext);

    m_undoAction = new QAction(tr("&Undo"), this);
    command = actionManager->registerAction(m_undoAction, Core::Constants::UNDO, submitContext);

    m_redoAction = new QAction(tr("&Redo"), this);
    command = actionManager->registerAction(m_redoAction, Core::Constants::REDO, submitContext);

    return true;
}

569
GitVersionControl *GitPlugin::gitVersionControl() const
570
{
571
    return static_cast<GitVersionControl *>(versionControl());
572
573
}

574
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
575
{
576
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
577
578
579
580
}

void GitPlugin::diffCurrentFile()
{
581
582
583
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    m_gitClient->diff(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile());
con's avatar
con committed
584
585
586
587
}

void GitPlugin::diffCurrentProject()
{
588
589
590
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
591
592
}

593
594
595
596
597
598
599
void GitPlugin::diffRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

con's avatar
con committed
600
601
void GitPlugin::logFile()
{
602
603
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
604
    m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
con's avatar
con committed
605
606
607
608
}

void GitPlugin::blameFile()
{
609
610
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
611
    const int lineNumber = VCSBase::VCSBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
612
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
613
614
615
616
}

void GitPlugin::logProject()
{
617
618
619
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
620
621
}

622
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
623
{
624
625
626
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    Core::FileChangeBlocker fcb(state.currentFile());
627
628
629
630
631
632
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

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

635
void GitPlugin::undoRepositoryChanges()
con's avatar
con committed
636
{
637
638
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
639
    const QString msg = tr("Undo all pending changes to the repository\n%1?").arg(QDir::toNativeSeparators(state.topLevel()));
640
641
    const QMessageBox::StandardButton answer
            = QMessageBox::question(m_core->mainWindow(),
642
                                    tr("Undo Changes"), msg,
643
644
645
646
                                    QMessageBox::Yes|QMessageBox::No,
                                    QMessageBox::No);
    if (answer == QMessageBox::No)
        return;
647
    m_gitClient->hardReset(state.topLevel(), QString());
con's avatar
con committed
648
649
}

650
void GitPlugin::stageFile()
con's avatar
con committed
651
{
652
653
654
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
655
656
}

657
658
void GitPlugin::unstageFile()
{
659
660
661
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return)
    m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
662
663
}

664
665
666
667
668
void GitPlugin::startAmendCommit()
{
    startCommit(true);
}

con's avatar
con committed
669
670
void GitPlugin::startCommit()
{
671
672
673
674
675
676
    startCommit(false);
}

void GitPlugin::startCommit(bool amend)
{

677
678
    if (VCSBase::VCSBaseSubmitEditor::raiseSubmitEditor())
        return;
679
    if (isCommitEditorOpen()) {
680
        VCSBase::VCSBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently being executed."));
con's avatar
con committed
681
682
683
        return;
    }

684
685
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
con's avatar
con committed
686
687
688

    QString errorMessage, commitTemplate;
    CommitData data;
689
    if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) {
690
        VCSBase::VCSBaseOutputWindow::instance()->append(errorMessage);
con's avatar
con committed
691
692
693
        return;
    }

694
695
    // Store repository for diff and the original list of
    // files to be able to unstage files the user unchecks
con's avatar
con committed
696
    m_submitRepository = data.panelInfo.repository;
697
    m_commitAmendSHA1 = data.amendSHA1;
698
    m_submitOrigCommitFiles = data.stagedFileNames();
699
    m_submitOrigDeleteFiles = data.stagedFileNames("deleted");
con's avatar
con committed
700
701

    // Start new temp file with message template
702
703
704
705
706
707
    Utils::TempFileSaver saver;
    // Keep the file alive, else it removes self and forgets its name
    saver.setAutoRemove(false);
    saver.write(commitTemplate.toLocal8Bit());
    if (!saver.finalize()) {
        VCSBase::VCSBaseOutputWindow::instance()->append(saver.errorString());
con's avatar
con committed
708
709
        return;
    }
710
    m_commitMessageFileName = saver.fileName();
711
    openSubmitEditor(m_commitMessageFileName, data, amend);
con's avatar
con committed
712
713
}

714
Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend)
con's avatar
con committed
715
{
hjk's avatar
hjk committed
716
    Core::IEditor *editor = m_core->editorManager()->openEditor(fileName, Constants::GITSUBMITEDITOR_ID,
717
                                                                Core::EditorManager::ModeSwitch);
con's avatar
con committed
718
    GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor);
hjk's avatar
hjk committed
719
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
720
721
    // The actions are for some reason enabled by the context switching
    // mechanism. Disable them correctly.
722
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentAction, m_diffSelectedFilesAction);
con's avatar
con committed
723
    submitEditor->setCommitData(cd);
724
    submitEditor->setCheckScriptWorkingDirectory(m_submitRepository);
725
726
727
728
    const QString title = amend ? tr("Amend %1").arg(cd.amendSHA1) : tr("Git Commit");
    submitEditor->setDisplayName(title);
    if (amend) // Allow for just correcting the message
        submitEditor->setEmptyFileListEnabled(true);
729
    connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList)));
con's avatar
con committed
730
731
732
733
734
735
    return editor;
}

void GitPlugin::submitCurrentLog()
{
    // Close the submit editor
736
    m_submitActionTriggered = true;
con's avatar
con committed
737
738
739
740
741
    QList<Core::IEditor*> editors;
    editors.push_back(m_core->editorManager()->currentEditor());
    m_core->editorManager()->closeEditors(editors);
}

742
bool GitPlugin::submitEditorAboutToClose(VCSBase::VCSBaseSubmitEditor *submitEditor)
con's avatar
con committed
743
{
744
745
746
747
    if (!isCommitEditorOpen())
        return false;
    Core::IFile *fileIFace = submitEditor->file();
    const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor);
con's avatar
con committed
748
749
750
751
752
    if (!fileIFace || !editor)
        return true;
    // Submit editor closing. Make it write out the commit message
    // and retrieve files
    const QFileInfo editorFile(fileIFace->fileName());
753
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
754
755
756
    // Paranoia!
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true;
757
758
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
759
    bool *promptData = m_settings.boolPointer(GitSettings::promptOnSubmitKey);
760
    const VCSBase::VCSBaseSubmitEditor::PromptSubmitResult answer =
Leena Miettinen's avatar
Leena Miettinen committed
761
            editor->promptSubmit(tr("Closing Git Editor"),
762
                                 tr("Do you want to commit the change?"),
763
                                 tr("Git will not accept this commit. Do you want to continue to edit it?"),
764
                                 promptData, !m_submitActionTriggered, false);
765
    m_submitActionTriggered = false;
con's avatar
con committed
766
    switch (answer) {
767
    case VCSBase::VCSBaseSubmitEditor::SubmitCanceled:
con's avatar
con committed
768
        return false; // Keep editing and change file
769
    case VCSBase::VCSBaseSubmitEditor::SubmitDiscarded:
770
        cleanCommitMessageFile();
con's avatar
con committed
771
772
773
774
775
776
        return true; // Cancel all
    default:
        break;
    }
    // Go ahead!
    const QStringList fileList = editor->checkedFiles();
777
    bool closeEditor = true;
778
    if (!fileList.empty() || !m_commitAmendSHA1.isEmpty()) {
con's avatar
con committed
779
        // get message & commit
780
781
        if (!m_core->fileManager()->saveFile(fileIFace))
            return false;
con's avatar
con committed
782

783
784
        closeEditor = m_gitClient->addAndCommit(m_submitRepository,
                                                editor->panelData(),
785
                                                m_commitAmendSHA1,
786
                                                m_commitMessageFileName,
787
                                                fileList,
788
789
                                                m_submitOrigCommitFiles,
                                                m_submitOrigDeleteFiles);
con's avatar
con committed
790
    }
791
    if (closeEditor)
792
        cleanCommitMessageFile();
793
    return closeEditor;
con's avatar
con committed
794
795
}

Robert Loehning's avatar
Robert Loehning committed
796
797
void GitPlugin::fetch()
{
Tobias Hunger's avatar
Tobias Hunger committed
798
    m_gitClient->synchronousFetch(currentState().topLevel(), QString());
Robert Loehning's avatar
Robert Loehning committed
799
800
}

con's avatar
con committed
801
802
void GitPlugin::pull()
{
803
804
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
805

806
    switch (m_gitClient->ensureStash(state.topLevel())) {
807
808
809
        case GitClient::StashUnchanged:
        case GitClient::Stashed:
        case GitClient::NotStashed:
810
            m_gitClient->synchronousPull(state.topLevel());
811
812
813
        default:
        break;
    }
con's avatar
con committed
814
815
816
817
}

void GitPlugin::push()
{
818
819
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
820
    m_gitClient->synchronousPush(state.topLevel());
821
822
}

823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
// Retrieve member function of git client stored as user data of action
static inline GitClientMemberFunc memberFunctionFromAction(const QObject *o)
{
    if (o) {
        if (const QAction *action = qobject_cast<const QAction *>(o)) {
            const QVariant v = action->data();
            if (qVariantCanConvert<GitClientMemberFunc>(v))
                return qVariantValue<GitClientMemberFunc>(v);
        }
    }
    return 0;
}

void GitPlugin::gitClientMemberFuncRepositoryAction()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    // Retrieve member function and invoke on repository
    GitClientMemberFunc func = memberFunctionFromAction(sender());
    QTC_ASSERT(func, return);
    (m_gitClient->*func)(state.topLevel());
}

846
847
848
849
850
851
852
void GitPlugin::cleanProject()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasProject(), return)
    cleanRepository(state.currentProjectPath());
}

853
854
855
856
void GitPlugin::cleanRepository()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
857
858
    cleanRepository(state.topLevel());
}
859

860
861
void GitPlugin::cleanRepository(const QString &directory)
{
862
863
864
865
    // Find files to be deleted
    QString errorMessage;
    QStringList files;
    QApplication::setOverrideCursor(Qt::WaitCursor);
866
    const bool gotFiles = m_gitClient->synchronousCleanList(directory, &files, &errorMessage);
867
868
869
870
871
872
873
874
875
    QApplication::restoreOverrideCursor();

    QWidget *parent = Core::ICore::instance()->mainWindow();
    if (!gotFiles) {
        QMessageBox::warning(parent, tr("Unable to retrieve file list"),
                             errorMessage);
        return;
    }
    if (files.isEmpty()) {
Leena Miettinen's avatar
Leena Miettinen committed
876
        QMessageBox::information(parent, tr("Repository Clean"),
877
878
879
880
881
882
883
884
885
886
887
888
                                 tr("The repository is clean."));
        return;
    }
    // Clean the trailing slash of directories
    const QChar slash = QLatin1Char('/');
    const QStringList::iterator end = files.end();
    for (QStringList::iterator it = files.begin(); it != end; ++it)
        if (it->endsWith(slash))
            it->truncate(it->size() - 1);

    // Show in dialog
    VCSBase::CleanDialog dialog(parent);
889
    dialog.setFileList(directory, files);
890
891
892
    dialog.exec();
}

893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
// If the file is modified in an editor, make sure it is saved.
static bool ensureFileSaved(const QString &fileName)
{
    const QList<Core::IEditor*> editors = Core::EditorManager::instance()->editorsForFileName(fileName);
    if (editors.isEmpty())
        return true;
    Core::IFile *file = editors.front()->file();
    if (!file || !file->isModified())
        return true;
    Core::FileManager *fm = Core::ICore::instance()->fileManager();
    bool canceled;
    QList<Core::IFile *> files;
    files << file;
    fm->saveModifiedFiles(files, &canceled);
    return !canceled;
}

void GitPlugin::applyCurrentFilePatch()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasPatchFile() && state.hasTopLevel(), return);
    const QString patchFile = state.currentPatchFile();
    if (!ensureFileSaved(patchFile))
        return;
    applyPatch(state.topLevel(), patchFile);
}

void GitPlugin::promptApplyPatch()
{
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return);
    applyPatch(state.topLevel(), QString());
}

void GitPlugin::applyPatch(const QString &workingDirectory, QString file)
{
    // Ensure user has been notified about pending changes
    switch (m_gitClient->ensureStash(workingDirectory)) {
    case GitClient::StashUnchanged:
    case GitClient::Stashed:
    case GitClient::NotStashed:
        break;
    default:
        return;
    }
    // Prompt for file
    if (file.isEmpty()) {
        const QString filter = tr("Patches (*.patch *.diff)");
        file =  QFileDialog::getOpenFileName(Core::ICore::instance()->mainWindow(),
Leena Miettinen's avatar
Leena Miettinen committed
942
                                             tr("Choose Patch"),
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
                                             QString(), filter);
        if (file.isEmpty())
            return;
    }
    // Run!
    VCSBase::VCSBaseOutputWindow *outwin = VCSBase::VCSBaseOutputWindow::instance();
    QString errorMessage;
    if (m_gitClient->synchronousApplyPatch(workingDirectory, file, &errorMessage)) {
        if (errorMessage.isEmpty()) {
            outwin->append(tr("Patch %1 successfully applied to %2").arg(file, workingDirectory));
        } else {
            outwin->append(errorMessage);
        }
    } else {
        outwin->appendError(errorMessage);
    }
}

961
962
void GitPlugin::stash()
{
963
964
965
966
967
968
969
970
971
972
973
    // Simple stash without prompt, reset repo.
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
    const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), 0);
    if (!id.isEmpty() && m_stashDialog)
        m_stashDialog->refresh(state.topLevel(), true);
}

void GitPlugin::stashSnapshot()
{
    // Prompt for description, restore immediately and keep on working.
974
975
    const VCSBase::VCSBasePluginState state = currentState();
    QTC_ASSERT(state.hasTopLevel(), return)
976
977
978
    const QString id = m_gitClient->synchronousStash(state.topLevel(), QString(), GitClient::StashImmediateRestore|GitClient::StashPromptDescription);
    if (!id.isEmpty() && m_stashDialog)
        m_stashDialog->refresh(state.topLevel(), true);
979
980
}

981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
// Create a non-modal dialog with refresh method or raise if it exists
template <class NonModalDialog>
    inline void showNonModalDialog(const QString &topLevel,
                                   QPointer<NonModalDialog> &dialog)
{
    if (dialog) {
        dialog->show();
        dialog->raise();
    } else {
        dialog = new NonModalDialog(Core::ICore::instance()->mainWindow());
        dialog->refresh(topLevel, true);
        dialog->show();
    }
}

996
997
void GitPlugin::branchList()
{
998
   showNonModalDialog(currentState().topLevel(), m_branchDialog);
999
1000
}

1001
1002
1003
1004
1005
void GitPlugin::remoteList()
{
    showNonModalDialog(currentState().topLevel(), m_remoteDialog);
}

1006
1007
void GitPlugin::stashList()
{
1008
    showNonModalDialog(currentState().topLevel(), m_stashDialog);
con's avatar
con committed
1009
1010
}

1011
void GitPlugin::updateActions(VCSBase::VCSBasePlugin::ActionState as)
con's avatar
con committed
1012
{
Friedemann Kleint's avatar
Friedemann Kleint committed
1013
    const bool repositoryEnabled = currentState().hasTopLevel();
1014
1015
1016
1017
    if (m_stashDialog)
        m_stashDialog->refresh(currentState().topLevel(), false);
    if (m_branchDialog)
        m_branchDialog->refresh(currentState().topLevel(), false);
1018
1019
    if (m_remoteDialog)
        m_remoteDialog->refresh(currentState().topLevel(), false);
1020

Friedemann Kleint's avatar
Friedemann Kleint committed
1021
    m_commandLocator->setEnabled(repositoryEnabled);
1022
    if (!enableMenuAction(as, m_menuAction))
1023
        return;
1024
1025
    // Note: This menu is visible if there is no repository. Only
    // 'Create Repository'/'Show' actions should be available.
1026
    const QString fileName = currentState().currentFileName();
1027
1028
    foreach (Utils::ParameterAction *fileAction, m_fileActions)
        fileAction->setParameter(fileName);
1029
1030
    // If the current file looks like a patch, offer to apply
    m_applyCurrentFilePatchAction->setParameter(currentState().currentPatchFileDisplayName());
con's avatar
con committed
1031

1032
    const QString projectName = currentState().currentProjectName();
1033
1034
1035
1036
1037
    foreach (Utils::ParameterAction *projectAction, m_projectActions)
        projectAction->setParameter(projectName);

    foreach (QAction *repositoryAction, m_repositoryActions)
        repositoryAction->setEnabled(repositoryEnabled);
1038

1039
1040
    // Prompts for repo.
    m_showAction->setEnabled(true);
con's avatar
con committed
1041
1042
1043
1044
}

void GitPlugin::showCommit()
{
1045
1046
    const VCSBase::VCSBasePluginState state = currentState();

con's avatar
con committed
1047
1048
1049
    if (!m_changeSelectionDialog)
        m_changeSelectionDialog = new ChangeSelectionDialog();

1050
1051
    if (state.hasTopLevel())
        m_changeSelectionDialog->setRepository(state.topLevel());
con's avatar
con committed
1052
1053
1054

    if (m_changeSelectionDialog->exec() != QDialog::Accepted)
        return;
1055
    const QString change = m_changeSelectionDialog->change();
1056
    if (change.isEmpty())
con's avatar
con committed
1057
1058
        return;

1059
    m_gitClient->show(m_changeSelectionDialog->repository(), change);
con's avatar
con committed
1060
1061
}