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

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

#include "changeselectiondialog.h"
#include "commitdata.h"
con's avatar
con committed
34
35
#include "gitclient.h"
#include "gitconstants.h"
hjk's avatar
hjk committed
36
#include "giteditor.h"
con's avatar
con committed
37
#include "gitsubmiteditor.h"
hjk's avatar
hjk committed
38
#include "gitversioncontrol.h"
39
#include "branchdialog.h"
40
#include "remotedialog.h"
41
#include "clonewizard.h"
42
#include "gitorious/gitoriousclonewizard.h"
43
#include "stashdialog.h"
44
#include "settingspage.h"
45
#include "resetdialog.h"
46
#include "mergetool.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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Petar Perisin's avatar
Petar Perisin committed
429
430
431
    createRepositoryAction(localRepositoryMenu,
                           tr("Reset..."), Core::Id("Git.Reset"),
                           globalcontext, false, SLOT(resetRepository()));
432

433
    // --------------
Petar Perisin's avatar
Petar Perisin committed
434
    localRepositoryMenu->addSeparator(globalcontext);
435

Petar Perisin's avatar
Petar Perisin committed
436
    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
437
                           tr("Branches..."), Core::Id("Git.BranchList"),
Yuchen Deng's avatar
Yuchen Deng committed
438
                           globalcontext, true, SLOT(branchList()));
439
440

    // --------------
Petar Perisin's avatar
Petar Perisin committed
441
    localRepositoryMenu->addSeparator(globalcontext);
442

Petar Perisin's avatar
Petar Perisin committed
443
    // "Patch" menu
Eike Ziller's avatar
Eike Ziller committed
444
    Core::ActionContainer *patchMenu = Core::ActionManager::createMenu(Core::Id("Git.PatchMenu"));
Petar Perisin's avatar
Petar Perisin committed
445
446
    patchMenu->menu()->setTitle(tr("&Patch"));
    localRepositoryMenu->addMenu(patchMenu);
447
448
449

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

Eike Ziller's avatar
Eike Ziller committed
458
    createRepositoryAction(patchMenu,
hjk's avatar
hjk committed
459
                           tr("Apply from File..."), Core::Id("Git.ApplyPatch"),
460
461
                           globalcontext, true, SLOT(promptApplyPatch()));

Petar Perisin's avatar
Petar Perisin committed
462
    // "Stash" menu
Eike Ziller's avatar
Eike Ziller committed
463
    Core::ActionContainer *stashMenu = Core::ActionManager::createMenu(Core::Id("Git.StashMenu"));
Petar Perisin's avatar
Petar Perisin committed
464
465
    stashMenu->menu()->setTitle(tr("&Stash"));
    localRepositoryMenu->addMenu(stashMenu);
466

Eike Ziller's avatar
Eike Ziller committed
467
    createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
468
                           tr("Stashes..."), Core::Id("Git.StashList"),
469
470
                           globalcontext, false, SLOT(stashList()));

471
    stashMenu->addSeparator(globalcontext);
472

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

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

483
    stashMenu->addSeparator(globalcontext);
484

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

Petar Perisin's avatar
Petar Perisin committed
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515

    /* \"Local Repository" menu */

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

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

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

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

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

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

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

Eike Ziller's avatar
Eike Ziller committed
520
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
521
                           tr("Log"), Core::Id("Git.Subversion.Log"),
522
523
                           globalcontext, false, &GitClient::subversionLog);

Eike Ziller's avatar
Eike Ziller committed
524
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
525
                           tr("Fetch"), Core::Id("Git.Subversion.Fetch"),
526
527
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

Petar Perisin's avatar
Petar Perisin committed
528
529
    // --------------
    remoteRepositoryMenu->addSeparator(globalcontext);
530

Petar Perisin's avatar
Petar Perisin committed
531
532
533
    createRepositoryAction(remoteRepositoryMenu,
                           tr("Manage Remotes..."), Core::Id("Git.RemoteList"),
                           globalcontext, false, SLOT(remoteList()));
534

Petar Perisin's avatar
Petar Perisin committed
535
    /* \"Remote Repository" menu */
Robert Loehning's avatar
Robert Loehning committed
536

Petar Perisin's avatar
Petar Perisin committed
537
    // --------------
538

Petar Perisin's avatar
Petar Perisin committed
539
540
541
542
    /*  "Git Tools" menu */
    Core::ActionContainer *gitToolsMenu = Core::ActionManager::createMenu(Core::Id("Git.GitToolsMenu"));
    gitToolsMenu->menu()->setTitle(tr("Git &Tools"));
    gitContainer->addMenu(gitToolsMenu);
543

Petar Perisin's avatar
Petar Perisin committed
544
545
546
    createRepositoryAction(gitToolsMenu,
                           tr("Gitk"), Core::Id("Git.LaunchGitK"),
                           globalcontext, true, &GitClient::launchGitK);
547

Petar Perisin's avatar
Petar Perisin committed
548
549
550
551
    m_repositoryBrowserAction
            = createRepositoryAction(gitToolsMenu,
                                     tr("Repository Browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;
552

Petar Perisin's avatar
Petar Perisin committed
553
    createRepositoryAction(gitToolsMenu,
554
555
556
                           tr("Merge Tool"), Core::Id("Git.MergeTool"),
                           globalcontext, true, SLOT(startMergeTool()));

Petar Perisin's avatar
Petar Perisin committed
557
558
559
    /* \"Git Tools" menu */

    // --------------
560
    gitContainer->addSeparator(globalcontext);
561

Petar Perisin's avatar
Petar Perisin committed
562
563
564
565
566
567
568
569
570
571
    m_showAction = new QAction(tr("Show..."), this);
    Core::Command *showCommitCommand = Core::ActionManager::registerAction(m_showAction, "Git.ShowCommit", globalcontext);
    connect(m_showAction, SIGNAL(triggered()), this, SLOT(showCommit()));
    gitContainer->addAction(showCommitCommand);

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

572
573
574
575
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
576
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
577
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
578
                                                    Core::Id(QLatin1String("Git.Snapshot.") + QString::number(i)),
579
                                                    globalcontext);
580
            gitContainer->addAction(tCommand);
581
582
583
        }
    }

con's avatar
con committed
584
    // Submit editor
585
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
586
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
587
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
588
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
589
590
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

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

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

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

600

Petar Perisin's avatar
Petar Perisin committed
601
    /* "Gerrit" */
602
    Gerrit::Internal::GerritPlugin *gp = new Gerrit::Internal::GerritPlugin(this);
Petar Perisin's avatar
Petar Perisin committed
603
    return gp->initialize(remoteRepositoryMenu);
con's avatar
con committed
604
605
}

606
GitVersionControl *GitPlugin::gitVersionControl() const
607
{
608
    return static_cast<GitVersionControl *>(versionControl());
609
610
}

611
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
612
{
613
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
614
615
}

616
617
618
619
620
void GitPlugin::submitEditorMerge(const QStringList &unmerged)
{
    m_gitClient->merge(m_submitRepository, unmerged);
}

con's avatar
con committed
621
622
void GitPlugin::diffCurrentFile()
{
hjk's avatar
hjk committed
623
    const VcsBase::VcsBasePluginState state = currentState();
624
    QTC_ASSERT(state.hasFile(), return);
625
    m_gitClient->diff(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile());
con's avatar
con committed
626
627
628
629
}

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
630
    const VcsBase::VcsBasePluginState state = currentState();
631
    QTC_ASSERT(state.hasProject(), return);
632
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
633
634
}

635
636
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
637
    const VcsBase::VcsBasePluginState state = currentState();
638
    QTC_ASSERT(state.hasTopLevel(), return);
639
640
641
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

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

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
651
    const VcsBase::VcsBasePluginState state = currentState();
652
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
653
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
654
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
655
656
657
658
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
659
    const VcsBase::VcsBasePluginState state = currentState();
660
    QTC_ASSERT(state.hasProject(), return);
661
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
662
663
}

664
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
665
{
hjk's avatar
hjk committed
666
    const VcsBase::VcsBasePluginState state = currentState();
667
    QTC_ASSERT(state.hasFile(), return);
668
    Core::FileChangeBlocker fcb(state.currentFile());
669
670
671
672
673
674
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

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

677
void GitPlugin::resetRepository()
con's avatar
con committed
678
{
hjk's avatar
hjk committed
679
    const VcsBase::VcsBasePluginState state = currentState();
680
    QTC_ASSERT(state.hasTopLevel(), return);
681
682
683
684

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

687
void GitPlugin::stageFile()
con's avatar
con committed
688
{
hjk's avatar
hjk committed
689
    const VcsBase::VcsBasePluginState state = currentState();
690
    QTC_ASSERT(state.hasFile(), return);
691
    m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
692
693
}

694
695
void GitPlugin::unstageFile()
{
hjk's avatar
hjk committed
696
    const VcsBase::VcsBasePluginState state = currentState();
697
    QTC_ASSERT(state.hasFile(), return);
698
    m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
699
700
}

701
702
703
704
705
void GitPlugin::startAmendCommit()
{
    startCommit(true);
}

con's avatar
con committed
706
707
void GitPlugin::startCommit()
{
708
709
710
711
712
    startCommit(false);
}

void GitPlugin::startCommit(bool amend)
{
hjk's avatar
hjk committed
713
    if (VcsBase::VcsBaseSubmitEditor::raiseSubmitEditor())
714
        return;
715
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
716
        VcsBase::VcsBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently being executed."));
con's avatar
con committed
717
718
719
        return;
    }

hjk's avatar
hjk committed
720
    const VcsBase::VcsBasePluginState state = currentState();
721
    QTC_ASSERT(state.hasTopLevel(), return);
con's avatar
con committed
722
723
724

    QString errorMessage, commitTemplate;
    CommitData data;
725
    if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) {
hjk's avatar
hjk committed
726
        VcsBase::VcsBaseOutputWindow::instance()->append(errorMessage);
con's avatar
con committed
727
728
729
        return;
    }

730
731
    // Store repository for diff and the original list of
    // files to be able to unstage files the user unchecks
con's avatar
con committed
732
    m_submitRepository = data.panelInfo.repository;
733
    m_commitAmendSHA1 = data.amendSHA1;
con's avatar
con committed
734
735

    // Start new temp file with message template
736
737
738
739
740
    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
741
        VcsBase::VcsBaseOutputWindow::instance()->append(saver.errorString());
con's avatar
con committed
742
743
        return;
    }
744
    m_commitMessageFileName = saver.fileName();
745
    openSubmitEditor(m_commitMessageFileName, data, amend);
con's avatar
con committed
746
747
}

748
Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend)
con's avatar
con committed
749
{
hjk's avatar
hjk committed
750
751
    Core::IEditor *editor = Core::EditorManager::openEditor(fileName, Constants::GITSUBMITEDITOR_ID,
                                                Core::EditorManager::ModeSwitch);
con's avatar
con committed
752
    GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor);
hjk's avatar
hjk committed
753
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
754
755
    // The actions are for some reason enabled by the context switching
    // mechanism. Disable them correctly.
756
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentAction, m_diffSelectedFilesAction);
con's avatar
con committed
757
    submitEditor->setCommitData(cd);
758
    submitEditor->setCheckScriptWorkingDirectory(m_submitRepository);
759
760
761
762
    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);
763
    connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList)));
764
    connect(submitEditor, SIGNAL(merge(QStringList)), this, SLOT(submitEditorMerge(QStringList)));
con's avatar
con committed
765
766
767
768
769
770
    return editor;
}

void GitPlugin::submitCurrentLog()
{
    // Close the submit editor
771
    m_submitActionTriggered = true;
Tobias Hunger's avatar
Tobias Hunger committed
772
    Core::ICore::editorManager()->closeEditor();
con's avatar
con committed
773
774
}

hjk's avatar
hjk committed
775
bool GitPlugin::submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *submitEditor)
con's avatar
con committed
776
{
777
778
    if (!isCommitEditorOpen())
        return false;
779
    Core::IDocument *editorDocument = submitEditor->document();
780
    const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor);
781
    if (!editorDocument || !editor)
con's avatar
con committed
782
783
784
        return true;
    // Submit editor closing. Make it write out the commit message
    // and retrieve files
785
    const QFileInfo editorFile(editorDocument->fileName());
786
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
787
788
789
    // Paranoia!
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true;
790
791
    // Prompt user. Force a prompt unless submit was actually invoked (that
    // is, the editor was closed or shutdown).
792
    bool *promptData = m_settings.boolPointer(GitSettings::promptOnSubmitKey);
hjk's avatar
hjk committed
793
    const VcsBase::VcsBaseSubmitEditor::PromptSubmitResult answer =
Leena Miettinen's avatar
Leena Miettinen committed
794
            editor->promptSubmit(tr("Closing Git Editor"),
795
                                 tr("Do you want to commit the change?"),
796
                                 tr("Git will not accept this commit. Do you want to continue to edit it?"),
797
                                 promptData, !m_submitActionTriggered, false);
798
    m_submitActionTriggered = false;
con's avatar
con committed
799
    switch (answer) {
hjk's avatar
hjk committed
800
    case VcsBase::VcsBaseSubmitEditor::SubmitCanceled:
con's avatar
con committed
801
        return false; // Keep editing and change file
hjk's avatar
hjk committed
802
    case VcsBase::VcsBaseSubmitEditor::SubmitDiscarded:
803
        cleanCommitMessageFile();
con's avatar
con committed
804
805
806
807
        return true; // Cancel all
    default:
        break;
    }