gitplugin.cpp 40.7 KB
Newer Older
1
/**************************************************************************
con's avatar
con committed
2
3
4
**
** This file is part of Qt Creator
**
hjk's avatar
hjk committed
5
** Copyright (c) 2012 Nokia Corporation and/or its subsidiary(-ies).
con's avatar
con committed
6
**
Eike Ziller's avatar
Eike Ziller committed
7
** Contact: http://www.qt-project.org/
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
**
29
**************************************************************************/
hjk's avatar
hjk committed
30

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

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

48
#include "gerrit/gerritplugin.h"
49

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

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

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

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

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

hjk's avatar
hjk committed
84
static const VcsBase::VcsBaseEditorParameters editorParameters[] = {
con's avatar
con committed
85
{
hjk's avatar
hjk committed
86
    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
    "application/vnd.nokia.text.scs_git_commandlog",
    "gitlog"},
hjk's avatar
hjk committed
92
{   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
    "application/vnd.nokia.text.scs_git_filelog",
    "gitfilelog"},
hjk's avatar
hjk committed
98
{   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
    "application/vnd.nokia.text.scs_git_annotation",
    "gitsannotate"},
hjk's avatar
hjk committed
104
{   VcsBase::DiffOutput,
105
106
    Git::Constants::GIT_DIFF_EDITOR_ID,
    Git::Constants::GIT_DIFF_EDITOR_DISPLAY_NAME,
107
    Git::Constants::C_GIT_DIFF_EDITOR,
con's avatar
con committed
108
109
110
111
    "text/x-patch","diff"}
};

// Utility to find a parameter set by type
hjk's avatar
hjk committed
112
static inline const VcsBase::VcsBaseEditorParameters *findType(int ie)
con's avatar
con committed
113
{
hjk's avatar
hjk committed
114
115
    const VcsBase::EditorContentType et = static_cast<VcsBase::EditorContentType>(ie);
    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() :
hjk's avatar
hjk committed
128
    VcsBase::VcsBasePlugin(QLatin1String(Git::Constants::GITSUBMITEDITOR_ID)),
Friedemann Kleint's avatar
Friedemann Kleint committed
129
    m_commandLocator(0),
con's avatar
con committed
130
131
132
133
134
    m_showAction(0),
    m_submitCurrentAction(0),
    m_diffSelectedFilesAction(0),
    m_undoAction(0),
    m_redoAction(0),
135
    m_menuAction(0),
136
    m_applyCurrentFilePatchAction(0),
con's avatar
con committed
137
138
    m_gitClient(0),
    m_changeSelectionDialog(0),
139
    m_submitActionTriggered(false)
con's avatar
con committed
140
141
{
    m_instance = this;
142
143
144
145
146
    const int mid = qRegisterMetaType<GitClientMemberFunc>();
    Q_UNUSED(mid)
    m_fileActions.reserve(10);
    m_projectActions.reserve(10);
    m_repositoryActions.reserve(15);
con's avatar
con committed
147
148
149
150
}

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

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

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

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

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

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

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

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

    m_gitClient = new GitClient(&m_settings);
275

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

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

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

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

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

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

Friedemann Kleint's avatar
Friedemann Kleint committed
296
297
298
299
300
    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
301
    //register actions
302
    Core::ActionContainer *toolsContainer =
Eike Ziller's avatar
Eike Ziller committed
303
        Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
con's avatar
con committed
304

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

310
    ParameterActionCommandPair parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
311
            = createFileAction(gitContainer,
Tobias Hunger's avatar
Tobias Hunger committed
312
                               tr("Blame Current File"), tr("Blame for \"%1\""),
hjk's avatar
hjk committed
313
                               Core::Id("Git.Blame"),
314
                               globalcontext, true, SLOT(blameFile()));
315
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+B") : tr("Alt+G,Alt+B")));
316

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

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

    // ------
331
    gitContainer->addSeparator(globalcontext);
332
333

    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
334
            = createFileAction(gitContainer,
335
                               tr("Stage File for Commit"), tr("Stage \"%1\" for Commit"),
hjk's avatar
hjk committed
336
                               Core::Id("Git.Stage"), globalcontext, true, SLOT(stageFile()));
337
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+A") : tr("Alt+G,Alt+A")));
338
339

    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
340
            = createFileAction(gitContainer,
341
                               tr("Unstage File from Commit"), tr("Unstage \"%1\" from Commit"),
hjk's avatar
hjk committed
342
                               Core::Id("Git.Unstage"), globalcontext, true, SLOT(unstageFile()));
343

344
    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
345
            = createFileAction(gitContainer,
346
                               tr("Undo Unstaged Changes"), tr("Undo Unstaged Changes for \"%1\""),
hjk's avatar
hjk committed
347
                               Core::Id("Git.UndoUnstaged"), globalcontext,
348
349
350
                               true, SLOT(undoUnstagedFileChanges()));

    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
351
            = createFileAction(gitContainer,
352
                               tr("Undo Uncommitted Changes"), tr("Undo Uncommitted Changes for \"%1\""),
hjk's avatar
hjk committed
353
                               Core::Id("Git.Undo"), globalcontext,
354
                               true, SLOT(undoFileChanges()));
355
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+U") : tr("Alt+G,Alt+U")));
356
357

    // ------------
358
    gitContainer->addSeparator(globalcontext);
con's avatar
con committed
359

360
    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
361
            = createProjectAction(gitContainer,
362
                                  tr("Diff Current Project"), tr("Diff Project \"%1\""),
hjk's avatar
hjk committed
363
                                  Core::Id("Git.DiffProject"),
364
365
                                  globalcontext, true,
                                  SLOT(diffCurrentProject()));
366
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+Shift+D") : tr("Alt+G,Alt+Shift+D")));
367
368

    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
369
            = createProjectAction(gitContainer,
370
                                  tr("Log Project"), tr("Log Project \"%1\""),
hjk's avatar
hjk committed
371
                                  Core::Id("Git.LogProject"), globalcontext, true,
372
                                  SLOT(logProject()));
373
    parameterActionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+K") : tr("Alt+G,Alt+K")));
374
375

    parameterActionCommand
Eike Ziller's avatar
Eike Ziller committed
376
                = createProjectAction(gitContainer,
377
                                      tr("Clean Project..."), tr("Clean Project \"%1\"..."),
hjk's avatar
hjk committed
378
                                      Core::Id("Git.CleanProject"), globalcontext,
379
                                      true, SLOT(cleanProject()));
con's avatar
con committed
380

381
382

    // --------------
383
    gitContainer->addSeparator(globalcontext);
con's avatar
con committed
384

Eike Ziller's avatar
Eike Ziller committed
385
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
386
                           tr("Diff"), Core::Id("Git.DiffRepository"),
387
                           globalcontext, true, SLOT(diffRepository()));
388

Eike Ziller's avatar
Eike Ziller committed
389
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
390
                           tr("Log"), Core::Id("Git.LogRepository"),
391
                           globalcontext, true, &GitClient::graphLog);
392

Eike Ziller's avatar
Eike Ziller committed
393
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
394
                           tr("Status"), Core::Id("Git.StatusRepository"),
395
                           globalcontext, true, &GitClient::status);
396

Eike Ziller's avatar
Eike Ziller committed
397
    createRepositoryAction(gitContainer,
398
399
                           tr("Reset..."), Core::Id("Git.Reset"),
                           globalcontext, false, SLOT(resetRepository()));
con's avatar
con committed
400

401

Eike Ziller's avatar
Eike Ziller committed
402
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
403
                           tr("Clean..."), Core::Id("Git.CleanRepository"),
404
405
                           globalcontext, true, SLOT(cleanRepository()));

406
    m_createRepositoryAction = new QAction(tr("Create Repository..."), this);
Eike Ziller's avatar
Eike Ziller committed
407
    Core::Command *createRepositoryCommand = Core::ActionManager::registerAction(m_createRepositoryAction, "Git.CreateRepository", globalcontext);
408
    connect(m_createRepositoryAction, SIGNAL(triggered()), this, SLOT(createRepository()));
409
    gitContainer->addAction(createRepositoryCommand);
410

411
    // --------------
412
    gitContainer->addSeparator(globalcontext);
413

Eike Ziller's avatar
Eike Ziller committed
414
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
415
                           tr("Launch gitk"), Core::Id("Git.LaunchGitK"),
416
                           globalcontext, true, &GitClient::launchGitK);
417

418
    m_repositoryBrowserAction
Eike Ziller's avatar
Eike Ziller committed
419
            = createRepositoryAction(gitContainer,
420
421
422
                                     tr("Launch repository browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;

Eike Ziller's avatar
Eike Ziller committed
423
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
424
                           tr("Branches..."), Core::Id("Git.BranchList"),
Yuchen Deng's avatar
Yuchen Deng committed
425
                           globalcontext, true, SLOT(branchList()));
426

Eike Ziller's avatar
Eike Ziller committed
427
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
428
                           tr("Remotes..."), Core::Id("Git.RemoteList"),
429
430
                           globalcontext, false, SLOT(remoteList()));

431
    m_showAction = new QAction(tr("Show..."), this);
Eike Ziller's avatar
Eike Ziller committed
432
    Core::Command *showCommitCommand = Core::ActionManager::registerAction(m_showAction, "Git.ShowCommit", globalcontext);
433
434
435
436
437
    connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
    gitContainer->addAction(showCommitCommand);


    // --------------
438
    gitContainer->addSeparator(globalcontext);
439

Eike Ziller's avatar
Eike Ziller committed
440
    Core::ActionContainer *patchMenu = Core::ActionManager::createMenu(Core::Id("Git.PatchMenu"));
441
442
443
444
445
    patchMenu->menu()->setTitle(tr("Patch"));
    gitContainer->addMenu(patchMenu);

    // Apply current file as patch is handled specially.
    parameterActionCommand =
Eike Ziller's avatar
Eike Ziller committed
446
            createParameterAction(patchMenu,
447
                                  tr("Apply from Editor"), tr("Apply \"%1\""),
hjk's avatar
hjk committed
448
                                  Core::Id("Git.ApplyCurrentFilePatch"),
449
450
451
452
453
                                  globalcontext, true);
    m_applyCurrentFilePatchAction = parameterActionCommand.first;
    connect(m_applyCurrentFilePatchAction, SIGNAL(triggered()), this,
            SLOT(applyCurrentFilePatch()));

Eike Ziller's avatar
Eike Ziller committed
454
    createRepositoryAction(patchMenu,
hjk's avatar
hjk committed
455
                           tr("Apply from File..."), Core::Id("Git.ApplyPatch"),
456
457
                           globalcontext, true, SLOT(promptApplyPatch()));

Eike Ziller's avatar
Eike Ziller committed
458
    Core::ActionContainer *stashMenu = Core::ActionManager::createMenu(Core::Id("Git.StashMenu"));
459
460
461
    stashMenu->menu()->setTitle(tr("Stash"));
    gitContainer->addMenu(stashMenu);

Eike Ziller's avatar
Eike Ziller committed
462
    createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
463
                           tr("Stashes..."), Core::Id("Git.StashList"),
464
465
                           globalcontext, false, SLOT(stashList()));

466
    stashMenu->addSeparator(globalcontext);
467

468
    ActionCommandPair actionCommand =
Eike Ziller's avatar
Eike Ziller committed
469
            createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
470
                                   tr("Stash"), Core::Id("Git.Stash"),
471
472
473
                                   globalcontext, true, SLOT(stash()));
    actionCommand.first->setToolTip(tr("Saves the current state of your work and resets the repository."));

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

479
    stashMenu->addSeparator(globalcontext);
480

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

Eike Ziller's avatar
Eike Ziller committed
486
    Core::ActionContainer *subversionMenu = Core::ActionManager::createMenu(Core::Id("Git.Subversion"));
487
488
489
    subversionMenu->menu()->setTitle(tr("Subversion"));
    gitContainer->addMenu(subversionMenu);

Eike Ziller's avatar
Eike Ziller committed
490
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
491
                           tr("Log"), Core::Id("Git.Subversion.Log"),
492
493
                           globalcontext, false, &GitClient::subversionLog);

Eike Ziller's avatar
Eike Ziller committed
494
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
495
                           tr("Fetch"), Core::Id("Git.Subversion.Fetch"),
496
497
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

498
    gitContainer->addSeparator(globalcontext);
499

500
    gitContainer->addSeparator(globalcontext);
501

Eike Ziller's avatar
Eike Ziller committed
502
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
503
                           tr("Fetch"), Core::Id("Git.Fetch"),
Robert Loehning's avatar
Robert Loehning committed
504
505
                           globalcontext, true, SLOT(fetch()));

Eike Ziller's avatar
Eike Ziller committed
506
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
507
                           tr("Pull"), Core::Id("Git.Pull"),
508
509
                           globalcontext, true, SLOT(pull()));

Eike Ziller's avatar
Eike Ziller committed
510
    actionCommand = createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
511
                                           tr("Push"), Core::Id("Git.Push"),
512
                                           globalcontext, true, SLOT(push()));
513

Eike Ziller's avatar
Eike Ziller committed
514
    actionCommand = createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
515
                                           tr("Commit..."), Core::Id("Git.Commit"),
516
                                           globalcontext, true, SLOT(startCommit()));
517
    actionCommand.second->setDefaultKeySequence(QKeySequence(Core::UseMacShortcuts ? tr("Meta+G,Meta+C") : tr("Alt+G,Alt+C")));
518

Eike Ziller's avatar
Eike Ziller committed
519
    createRepositoryAction(gitContainer,
hjk's avatar
hjk committed
520
                           tr("Amend Last Commit..."), Core::Id("Git.AmendCommit"),
521
522
                           globalcontext, true, SLOT(startAmendCommit()));

523
    // Subversion in a submenu.
524
    gitContainer->addSeparator(globalcontext);
525

526
527
528
529
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
530
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
531
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
532
                                                    Core::Id(QLatin1String("Git.Snapshot.") + QString::number(i)),
533
                                                    globalcontext);
534
            gitContainer->addAction(tCommand);
535
536
537
        }
    }

con's avatar
con committed
538
    // Submit editor
539
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
540
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
541
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
542
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
543
544
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

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

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

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

554
555
556

    Gerrit::Internal::GerritPlugin *gp = new Gerrit::Internal::GerritPlugin(this);
    return gp->initialize(gitContainer);
con's avatar
con committed
557
558
}

559
GitVersionControl *GitPlugin::gitVersionControl() const
560
{
561
    return static_cast<GitVersionControl *>(versionControl());
562
563
}

564
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
565
{
566
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
567
568
569
570
}

void GitPlugin::diffCurrentFile()
{
hjk's avatar
hjk committed
571
    const VcsBase::VcsBasePluginState state = currentState();
572
    QTC_ASSERT(state.hasFile(), return);
573
    m_gitClient->diff(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile());
con's avatar
con committed
574
575
576
577
}

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
578
    const VcsBase::VcsBasePluginState state = currentState();
579
    QTC_ASSERT(state.hasProject(), return);
580
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
581
582
}

583
584
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
585
    const VcsBase::VcsBasePluginState state = currentState();
586
    QTC_ASSERT(state.hasTopLevel(), return);
587
588
589
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

con's avatar
con committed
590
591
void GitPlugin::logFile()
{
hjk's avatar
hjk committed
592
    const VcsBase::VcsBasePluginState state = currentState();
593
    QTC_ASSERT(state.hasFile(), return);
594
    m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
con's avatar
con committed
595
596
597
598
}

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
599
    const VcsBase::VcsBasePluginState state = currentState();
600
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
601
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
602
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
603
604
605
606
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
607
    const VcsBase::VcsBasePluginState state = currentState();
608
    QTC_ASSERT(state.hasProject(), return);
609
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
610
611
}

612
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
613
{
hjk's avatar
hjk committed
614
    const VcsBase::VcsBasePluginState state = currentState();
615
    QTC_ASSERT(state.hasFile(), return);
616
    Core::FileChangeBlocker fcb(state.currentFile());
617
618
619
620
621
622
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

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

625
void GitPlugin::resetRepository()
con's avatar
con committed
626
{
hjk's avatar
hjk committed
627
    const VcsBase::VcsBasePluginState state = currentState();
628
    QTC_ASSERT(state.hasTopLevel(), return);
629
630
631
632

    ResetDialog dialog;
    if (dialog.runDialog(state.topLevel()))
        m_gitClient->hardReset(state.topLevel(), dialog.commit());
con's avatar
con committed
633
634
}

635
void GitPlugin::stageFile()
con's avatar
con committed
636
{
hjk's avatar
hjk committed
637
    const VcsBase::VcsBasePluginState state = currentState();
638
    QTC_ASSERT(state.hasFile(), return);
639
    m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
640
641
}

642
643
void GitPlugin::unstageFile()
{
hjk's avatar
hjk committed
644
    const VcsBase::VcsBasePluginState state = currentState();
645
    QTC_ASSERT(state.hasFile(), return);
646
    m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
647
648
}

649
650
651
652
653
void GitPlugin::startAmendCommit()
{
    startCommit(true);
}

con's avatar
con committed
654
655
void GitPlugin::startCommit()
{
656
657
658
659
660
    startCommit(false);
}

void GitPlugin::startCommit(bool amend)
{
hjk's avatar
hjk committed
661
    if (VcsBase::VcsBaseSubmitEditor::raiseSubmitEditor())
662
        return;
663
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
664
        VcsBase::VcsBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently being executed."));
con's avatar
con committed
665
666
667
        return;
    }

hjk's avatar
hjk committed
668
    const VcsBase::VcsBasePluginState state = currentState();
669
    QTC_ASSERT(state.hasTopLevel(), return);
con's avatar
con committed
670
671
672

    QString errorMessage, commitTemplate;
    CommitData data;
673
    if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) {
hjk's avatar
hjk committed
674
        VcsBase::VcsBaseOutputWindow::instance()->append(errorMessage);
con's avatar
con committed
675
676
677
        return;
    }

678
679
    // Store repository for diff and the original list of
    // files to be able to unstage files the user unchecks
con's avatar
con committed
680
    m_submitRepository = data.panelInfo.repository;
681
    m_commitAmendSHA1 = data.amendSHA1;
con's avatar
con committed
682
683

    // Start new temp file with message template
684
685
686
687
688
    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()) {
hjk's avatar
hjk committed
689
        VcsBase::VcsBaseOutputWindow::instance()->append(saver.errorString());
con's avatar
con committed
690
691
        return;
    }
692
    m_commitMessageFileName = saver.fileName();
693
    openSubmitEditor(m_commitMessageFileName, data, amend);
con's avatar
con committed
694
695
}

696
Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend)
con's avatar
con committed
697
{
hjk's avatar
hjk committed
698
699
    Core::IEditor *editor = Core::EditorManager::openEditor(fileName, Constants::GITSUBMITEDITOR_ID,
                                                Core::EditorManager::ModeSwitch);
con's avatar
con committed
700
    GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor);
hjk's avatar
hjk committed
701
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
702
703
    // The actions are for some reason enabled by the context switching
    // mechanism. Disable them correctly.
704
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentAction, m_diffSelectedFilesAction);
con's avatar
con committed
705
    submitEditor->setCommitData(cd);
706
    submitEditor->setCheckScriptWorkingDirectory(m_submitRepository);
707
708
709
710
    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);
711
    connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList)));
con's avatar
con committed
712
713
714
715
716
717
    return editor;
}

void GitPlugin::submitCurrentLog()
{
    // Close the submit editor
718
    m_submitActionTriggered = true;
Tobias Hunger's avatar
Tobias Hunger committed
719
    Core::ICore::editorManager()->closeEditor();
con's avatar
con committed
720
721
}

hjk's avatar
hjk committed
722
bool GitPlugin::submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *submitEditor)
con's avatar
con committed
723
{
724
725
    if (!isCommitEditorOpen())
        return false;
726
    Core::IDocument *editorDocument = submitEditor->document();
727
    const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor);
728
    if (!editorDocument || !editor)
con's avatar
con committed
729
730
731
        return true;
    // Submit editor closing. Make it write out the commit message
    // and retrieve files
732
    const QFileInfo editorFile(editorDocument->fileName());
733
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
734
735
736
    // Paranoia!
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true;
737
738
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
739
    bool *promptData = m_settings.boolPointer(GitSettings::promptOnSubmitKey);
hjk's avatar
hjk committed
740
    const VcsBase::VcsBaseSubmitEditor::PromptSubmitResult answer =
Leena Miettinen's avatar
Leena Miettinen committed
741
            editor->promptSubmit(tr("Closing Git Editor"),
742
                                 tr("Do you want to commit the change?"),
743
                                 tr("Git will not accept this commit. Do you want to continue to edit it?"),
744
                                 promptData, !m_submitActionTriggered, false);
745
    m_submitActionTriggered = false;
con's avatar
con committed
746
    switch (answer) {