gitplugin.cpp 48.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
552
553
554
555
556
557
558
559
560
    parameterActionCommand
            = createFileAction(gitToolsMenu,
                               tr("Gitk Current File"), tr("Gitk of \"%1\""),
                               Core::Id("Git.GitkFile"), globalcontext, true, SLOT(gitkForCurrentFile()));

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

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

Petar Perisin's avatar
Petar Perisin committed
561
562
563
564
    m_repositoryBrowserAction
            = createRepositoryAction(gitToolsMenu,
                                     tr("Repository Browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;
565

Petar Perisin's avatar
Petar Perisin committed
566
    createRepositoryAction(gitToolsMenu,
567
568
569
                           tr("Merge Tool"), Core::Id("Git.MergeTool"),
                           globalcontext, true, SLOT(startMergeTool()));

Petar Perisin's avatar
Petar Perisin committed
570
571
572
    /* \"Git Tools" menu */

    // --------------
573
    gitContainer->addSeparator(globalcontext);
574

Petar Perisin's avatar
Petar Perisin committed
575
576
577
578
579
580
581
582
583
584
    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);

585
586
587
588
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
589
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
590
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
591
                                                    Core::Id(QLatin1String("Git.Snapshot.") + QString::number(i)),
592
                                                    globalcontext);
593
            gitContainer->addAction(tCommand);
594
595
596
        }
    }

con's avatar
con committed
597
    // Submit editor
598
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
599
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
600
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
601
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
602
603
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

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

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

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

613

Petar Perisin's avatar
Petar Perisin committed
614
    /* "Gerrit" */
615
    Gerrit::Internal::GerritPlugin *gp = new Gerrit::Internal::GerritPlugin(this);
Petar Perisin's avatar
Petar Perisin committed
616
    return gp->initialize(remoteRepositoryMenu);
con's avatar
con committed
617
618
}

619
GitVersionControl *GitPlugin::gitVersionControl() const
620
{
621
    return static_cast<GitVersionControl *>(versionControl());
622
623
}

624
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
625
{
626
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
627
628
}

629
630
631
632
633
void GitPlugin::submitEditorMerge(const QStringList &unmerged)
{
    m_gitClient->merge(m_submitRepository, unmerged);
}

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

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
643
    const VcsBase::VcsBasePluginState state = currentState();
644
    QTC_ASSERT(state.hasProject(), return);
645
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
646
647
}

648
649
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
650
    const VcsBase::VcsBasePluginState state = currentState();
651
    QTC_ASSERT(state.hasTopLevel(), return);
652
653
654
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

con's avatar
con committed
655
656
void GitPlugin::logFile()
{
hjk's avatar
hjk committed
657
    const VcsBase::VcsBasePluginState state = currentState();
658
    QTC_ASSERT(state.hasFile(), return);
659
    m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
con's avatar
con committed
660
661
662
663
}

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
664
    const VcsBase::VcsBasePluginState state = currentState();
665
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
666
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
667
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
668
669
670
671
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
672
    const VcsBase::VcsBasePluginState state = currentState();
673
    QTC_ASSERT(state.hasProject(), return);
674
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
675
676
}

677
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
678
{
hjk's avatar
hjk committed
679
    const VcsBase::VcsBasePluginState state = currentState();
680
    QTC_ASSERT(state.hasFile(), return);
681
    Core::FileChangeBlocker fcb(state.currentFile());
682
683
684
685
686
687
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

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

690
void GitPlugin::resetRepository()
con's avatar
con committed
691
{
hjk's avatar
hjk committed
692
    const VcsBase::VcsBasePluginState state = currentState();
693
    QTC_ASSERT(state.hasTopLevel(), return);
694
695
696

    ResetDialog dialog;
    if (dialog.runDialog(state.topLevel()))
Petar Perisin's avatar
Petar Perisin committed
697
698
699
700
701
702
703
704
        switch (dialog.resetType()) {
        case HardReset:
            m_gitClient->hardReset(state.topLevel(), dialog.commit());
            break;
        case SoftReset:
            m_gitClient->softReset(state.topLevel(), dialog.commit());
            break;
        }
con's avatar
con committed
705
706
}

707
void GitPlugin::stageFile()
con's avatar
con committed
708
{
hjk's avatar
hjk committed
709
    const VcsBase::VcsBasePluginState state = currentState();
710
    QTC_ASSERT(state.hasFile(), return);
711
    m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
712
713
}

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

Petar Perisin's avatar
Petar Perisin committed
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
void GitPlugin::gitkForCurrentFile()
{
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return);
    m_gitClient->launchGitK(state.currentFileTopLevel(), state.relativeCurrentFile());
}

void GitPlugin::gitkForCurrentFolder()
{
    const VcsBase::VcsBasePluginState state = currentState();
    QTC_ASSERT(state.hasFile(), return);

    /*
     *  entire lower part of the code can be easily replaced with one line:
     *
     *  m_gitClient->launchGitK(dir.currentFileDirectory(), QLatin1String("."));
     *
     *  However, there is a bug in gitk in version 1.7.9.5, and if you run above
     *  command, there will be no documents listed in lower right section.
     *
     *  This is why I use lower combination in order to avoid this problems in gitk.
     *
     *  Git version 1.7.10.4 does not have this issue, and it can easily use
     *  one line command mentioned above.
     *
     */
    QDir dir(state.currentFileDirectory());
    if (QFileInfo(dir,QLatin1String(".git")).exists() || dir.cd(QLatin1String(".git")))
        m_gitClient->launchGitK(state.currentFileDirectory());
    else {
        QString folderName = dir.absolutePath();
        dir.cdUp();
        folderName = folderName.remove(0, dir.absolutePath().length() + 1);
        m_gitClient->launchGitK(dir.absolutePath(), folderName);
    }
}

758
759
760
761
762
void GitPlugin::startAmendCommit()
{
    startCommit(true);
}

con's avatar
con committed
763
764
void GitPlugin::startCommit()
{
765
766
767
768
769
    startCommit(false);
}

void GitPlugin::startCommit(bool amend)
{
hjk's avatar
hjk committed
770
    if (VcsBase::VcsBaseSubmitEditor::raiseSubmitEditor())
771
        return;
772
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
773
        VcsBase::VcsBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently being executed."));
con's avatar
con committed
774
775
776
        return;
    }

hjk's avatar
hjk committed
777
    const VcsBase::VcsBasePluginState state = currentState();
778
    QTC_ASSERT(state.hasTopLevel(), return);
con's avatar
con committed
779
780
781

    QString errorMessage, commitTemplate;
    CommitData data;
782
    if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) {
hjk's avatar
hjk committed
783
        VcsBase::VcsBaseOutputWindow::instance()->append(errorMessage);
con's avatar
con committed
784
785
786
        return;
    }

787
788
    // Store repository for diff and the original list of
    // files to be able to unstage files the user unchecks
con's avatar
con committed
789
    m_submitRepository = data.panelInfo.repository;
790
    m_commitAmendSHA1 = data.amendSHA1;
con's avatar
con committed
791
792

    // Start new temp file with message template
793
794
795
796
797
    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
798
        VcsBase::VcsBaseOutputWindow::instance()->append(saver.errorString());
con's avatar
con committed
799
800
        return;
    }
801
    m_commitMessageFileName = saver.fileName();
802
    openSubmitEditor(m_commitMessageFileName, data, amend);
con's avatar
con committed
803
804
}

805
Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend)
con's avatar
con committed
806
{
hjk's avatar
hjk committed
807
808
    Core::IEditor *editor = Core::EditorManager::openEditor(fileName, Constants::GITSUBMITEDITOR_ID,
                                                Core::EditorManager::ModeSwitch);
con's avatar
con committed
809
    GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor);
hjk's avatar
hjk committed
810
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
811
812
    // The actions are for some reason enabled by the context switching
    // mechanism. Disable them correctly.
813
    submitEditor->registerActions(m_undoAction, m_redoAction, m_submitCurrentAction, m_diffSelectedFilesAction);
con's avatar
con committed
814
    submitEditor->setCommitData(cd);
815
    submitEditor->setCheckScriptWorkingDirectory(m_submitRepository);
816
817
    const QString title = amend ? tr("Amend %1").arg(cd.amendSHA1) : tr("Git Commit");
    submitEditor->setDisplayName(title);
818
    submitEditor->setAmend(amend);
819
    connect(submitEditor, SIGNAL(diff(QStringList,QStringList)), this, SLOT(submitEditorDiff(QStringList,QStringList)));
820
    connect(submitEditor, SIGNAL(merge(QStringList)), this, SLOT(submitEditorMerge(QStringList)));
con's avatar
con committed
821
822
823
824
825
826
    return editor;
}

void GitPlugin::submitCurrentLog()
{
    // Close the submit editor
827
    m_submitActionTriggered = true;
Tobias Hunger's avatar
Tobias Hunger committed
828
    Core::ICore::editorManager()->closeEditor();
con's avatar
con committed
829
830
}

hjk's avatar
hjk committed
831
bool GitPlugin::submitEditorAboutToClose(VcsBase::VcsBaseSubmitEditor *submitEditor)
con's avatar
con committed
832
{
833
834
    if (!isCommitEditorOpen())
        return false;
835
    Core::IDocument *editorDocument = submitEditor->document();
836
    const GitSubmitEditor *editor = qobject_cast<GitSubmitEditor *>(submitEditor);
837
    if (!editorDocument || !editor)
con's avatar
con committed
838
839
840
        return true;
    // Submit editor closing. Make it write out the commit message
    // and retrieve files
841
    const QFileInfo editorFile(editorDocument->fileName());
842
    const QFileInfo changeFile(m_commitMessageFileName);
con's avatar
con committed
843
844
845
    // Paranoia!
    if (editorFile.absoluteFilePath() != changeFile.absoluteFilePath())
        return true;