gitplugin.cpp 52.8 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
179
    Git::Constants::C_GITSUBMITEDITOR,
    VcsBase::VcsBaseSubmitEditorParameters::DiffRows
con's avatar
con committed
180
181
};

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

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

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

    m_gitClient = new GitClient(&m_settings);
276

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

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

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

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

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

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

Friedemann Kleint's avatar
Friedemann Kleint committed
297
    const QString prefix = QLatin1String("git");
298
    m_commandLocator = new Locator::CommandLocator("Git", prefix, prefix);
Friedemann Kleint's avatar
Friedemann Kleint committed
299
300
    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
429
    // --------------
    localRepositoryMenu->addSeparator(globalcontext);
430

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

435
436
437
438
439
440
441
442
    createRepositoryAction(localRepositoryMenu,
                           tr("Revert Single Commit..."), Core::Id("Git.Revert"),
                           globalcontext, true, SLOT(startRevertCommit()));

    createRepositoryAction(localRepositoryMenu,
                           tr("Cherry-Pick Commit..."), Core::Id("Git.CherryPick"),
                           globalcontext, true, SLOT(startCherryPickCommit()));

443
    // --------------
Petar Perisin's avatar
Petar Perisin committed
444
    localRepositoryMenu->addSeparator(globalcontext);
445

Petar Perisin's avatar
Petar Perisin committed
446
    createRepositoryAction(localRepositoryMenu,
hjk's avatar
hjk committed
447
                           tr("Branches..."), Core::Id("Git.BranchList"),
Yuchen Deng's avatar
Yuchen Deng committed
448
                           globalcontext, true, SLOT(branchList()));
449
450

    // --------------
Petar Perisin's avatar
Petar Perisin committed
451
    localRepositoryMenu->addSeparator(globalcontext);
452

Petar Perisin's avatar
Petar Perisin committed
453
    // "Patch" menu
Eike Ziller's avatar
Eike Ziller committed
454
    Core::ActionContainer *patchMenu = Core::ActionManager::createMenu(Core::Id("Git.PatchMenu"));
Petar Perisin's avatar
Petar Perisin committed
455
456
    patchMenu->menu()->setTitle(tr("&Patch"));
    localRepositoryMenu->addMenu(patchMenu);
457
458
459

    // Apply current file as patch is handled specially.
    parameterActionCommand =
Eike Ziller's avatar
Eike Ziller committed
460
            createParameterAction(patchMenu,
461
                                  tr("Apply from Editor"), tr("Apply \"%1\""),
hjk's avatar
hjk committed
462
                                  Core::Id("Git.ApplyCurrentFilePatch"),
463
464
465
466
467
                                  globalcontext, true);
    m_applyCurrentFilePatchAction = parameterActionCommand.first;
    connect(m_applyCurrentFilePatchAction, SIGNAL(triggered()), this,
            SLOT(applyCurrentFilePatch()));

Eike Ziller's avatar
Eike Ziller committed
468
    createRepositoryAction(patchMenu,
hjk's avatar
hjk committed
469
                           tr("Apply from File..."), Core::Id("Git.ApplyPatch"),
470
471
                           globalcontext, true, SLOT(promptApplyPatch()));

Petar Perisin's avatar
Petar Perisin committed
472
    // "Stash" menu
Eike Ziller's avatar
Eike Ziller committed
473
    Core::ActionContainer *stashMenu = Core::ActionManager::createMenu(Core::Id("Git.StashMenu"));
Petar Perisin's avatar
Petar Perisin committed
474
475
    stashMenu->menu()->setTitle(tr("&Stash"));
    localRepositoryMenu->addMenu(stashMenu);
476

Eike Ziller's avatar
Eike Ziller committed
477
    createRepositoryAction(stashMenu,
hjk's avatar
hjk committed
478
                           tr("Stashes..."), Core::Id("Git.StashList"),
479
480
                           globalcontext, false, SLOT(stashList()));

481
    stashMenu->addSeparator(globalcontext);
482

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

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

493
    stashMenu->addSeparator(globalcontext);
494

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

Petar Perisin's avatar
Petar Perisin committed
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525

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

Eike Ziller's avatar
Eike Ziller committed
530
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
531
                           tr("Log"), Core::Id("Git.Subversion.Log"),
532
533
                           globalcontext, false, &GitClient::subversionLog);

Eike Ziller's avatar
Eike Ziller committed
534
    createRepositoryAction(subversionMenu,
hjk's avatar
hjk committed
535
                           tr("Fetch"), Core::Id("Git.Subversion.Fetch"),
536
537
                           globalcontext, false, &GitClient::synchronousSubversionFetch);

Petar Perisin's avatar
Petar Perisin committed
538
539
    // --------------
    remoteRepositoryMenu->addSeparator(globalcontext);
540

Petar Perisin's avatar
Petar Perisin committed
541
542
543
    createRepositoryAction(remoteRepositoryMenu,
                           tr("Manage Remotes..."), Core::Id("Git.RemoteList"),
                           globalcontext, false, SLOT(remoteList()));
544

Petar Perisin's avatar
Petar Perisin committed
545
    /* \"Remote Repository" menu */
Robert Loehning's avatar
Robert Loehning committed
546

Petar Perisin's avatar
Petar Perisin committed
547
    // --------------
548

Petar Perisin's avatar
Petar Perisin committed
549
550
551
552
    /*  "Git Tools" menu */
    Core::ActionContainer *gitToolsMenu = Core::ActionManager::createMenu(Core::Id("Git.GitToolsMenu"));
    gitToolsMenu->menu()->setTitle(tr("Git &Tools"));
    gitContainer->addMenu(gitToolsMenu);
553

Petar Perisin's avatar
Petar Perisin committed
554
555
556
    createRepositoryAction(gitToolsMenu,
                           tr("Gitk"), Core::Id("Git.LaunchGitK"),
                           globalcontext, true, &GitClient::launchGitK);
557

Petar Perisin's avatar
Petar Perisin committed
558
559
560
561
562
563
564
565
566
567
568
569
570
    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
571
572
573
574
    m_repositoryBrowserAction
            = createRepositoryAction(gitToolsMenu,
                                     tr("Repository Browser"), Core::Id("Git.LaunchRepositoryBrowser"),
                                     globalcontext, true, &GitClient::launchRepositoryBrowser).first;
575

Petar Perisin's avatar
Petar Perisin committed
576
    createRepositoryAction(gitToolsMenu,
577
578
579
                           tr("Merge Tool"), Core::Id("Git.MergeTool"),
                           globalcontext, true, SLOT(startMergeTool()));

Petar Perisin's avatar
Petar Perisin committed
580
581
582
    /* \"Git Tools" menu */

    // --------------
583
    gitContainer->addSeparator(globalcontext);
584

Petar Perisin's avatar
Petar Perisin committed
585
586
587
588
589
590
591
592
593
594
    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);

595
596
597
598
    if (0) {
        const QList<QAction*> snapShotActions = createSnapShotTestActions();
        const int count = snapShotActions.size();
        for (int i = 0; i < count; i++) {
599
            Core::Command *tCommand
Eike Ziller's avatar
Eike Ziller committed
600
                    = Core::ActionManager::registerAction(snapShotActions.at(i),
hjk's avatar
hjk committed
601
                                                    Core::Id(QLatin1String("Git.Snapshot.") + QString::number(i)),
602
                                                    globalcontext);
603
            gitContainer->addAction(tCommand);
604
605
606
        }
    }

con's avatar
con committed
607
    // Submit editor
608
    Core::Context submitContext(Constants::C_GITSUBMITEDITOR);
hjk's avatar
hjk committed
609
    m_submitCurrentAction = new QAction(VcsBase::VcsBaseSubmitEditor::submitIcon(), tr("Commit"), this);
Eike Ziller's avatar
Eike Ziller committed
610
    Core::Command *command = Core::ActionManager::registerAction(m_submitCurrentAction, Constants::SUBMIT_CURRENT, submitContext);
611
    command->setAttribute(Core::Command::CA_UpdateText);
con's avatar
con committed
612
613
    connect(m_submitCurrentAction, SIGNAL(triggered()), this, SLOT(submitCurrentLog()));

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

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

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

623

Petar Perisin's avatar
Petar Perisin committed
624
    /* "Gerrit" */
625
    Gerrit::Internal::GerritPlugin *gp = new Gerrit::Internal::GerritPlugin(this);
Petar Perisin's avatar
Petar Perisin committed
626
    return gp->initialize(remoteRepositoryMenu);
con's avatar
con committed
627
628
}

629
GitVersionControl *GitPlugin::gitVersionControl() const
630
{
631
    return static_cast<GitVersionControl *>(versionControl());
632
633
}

634
void GitPlugin::submitEditorDiff(const QStringList &unstaged, const QStringList &staged)
con's avatar
con committed
635
{
636
    m_gitClient->diff(m_submitRepository, QStringList(), unstaged, staged);
con's avatar
con committed
637
638
}

639
640
641
642
643
void GitPlugin::submitEditorMerge(const QStringList &unmerged)
{
    m_gitClient->merge(m_submitRepository, unmerged);
}

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

void GitPlugin::diffCurrentProject()
{
hjk's avatar
hjk committed
653
    const VcsBase::VcsBasePluginState state = currentState();
654
    QTC_ASSERT(state.hasProject(), return);
655
    m_gitClient->diff(state.currentProjectTopLevel(), QStringList(), state.relativeCurrentProject());
con's avatar
con committed
656
657
}

658
659
void GitPlugin::diffRepository()
{
hjk's avatar
hjk committed
660
    const VcsBase::VcsBasePluginState state = currentState();
661
    QTC_ASSERT(state.hasTopLevel(), return);
662
663
664
    m_gitClient->diff(state.topLevel(), QStringList(), QStringList());
}

con's avatar
con committed
665
666
void GitPlugin::logFile()
{
hjk's avatar
hjk committed
667
    const VcsBase::VcsBasePluginState state = currentState();
668
    QTC_ASSERT(state.hasFile(), return);
669
    m_gitClient->log(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()), true);
con's avatar
con committed
670
671
672
673
}

void GitPlugin::blameFile()
{
hjk's avatar
hjk committed
674
    const VcsBase::VcsBasePluginState state = currentState();
675
    QTC_ASSERT(state.hasFile(), return);
hjk's avatar
hjk committed
676
    const int lineNumber = VcsBase::VcsBaseEditorWidget::lineNumberOfCurrentEditor(state.currentFile());
677
    m_gitClient->blame(state.currentFileTopLevel(), QStringList(), state.relativeCurrentFile(), QString(), lineNumber);
con's avatar
con committed
678
679
680
681
}

void GitPlugin::logProject()
{
hjk's avatar
hjk committed
682
    const VcsBase::VcsBasePluginState state = currentState();
683
    QTC_ASSERT(state.hasProject(), return);
684
    m_gitClient->log(state.currentProjectTopLevel(), state.relativeCurrentProject());
con's avatar
con committed
685
686
}

687
void GitPlugin::undoFileChanges(bool revertStaging)
con's avatar
con committed
688
{
hjk's avatar
hjk committed
689
    const VcsBase::VcsBasePluginState state = currentState();
690
    QTC_ASSERT(state.hasFile(), return);
691
    Core::FileChangeBlocker fcb(state.currentFile());
692
693
694
695
696
697
    m_gitClient->revert(QStringList(state.currentFile()), revertStaging);
}

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

700
void GitPlugin::resetRepository()
con's avatar
con committed
701
{
hjk's avatar
hjk committed
702
    const VcsBase::VcsBasePluginState state = currentState();
703
    QTC_ASSERT(state.hasTopLevel(), return);
704
705
706

    ResetDialog dialog;
    if (dialog.runDialog(state.topLevel()))
Petar Perisin's avatar
Petar Perisin committed
707
708
709
710
711
712
713
714
        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
715
716
}

717
718
void GitPlugin::startRevertCommit()
{
Orgad Shaneh's avatar
Orgad Shaneh committed
719
720
721
722
723
724
725
726
    const VcsBase::VcsBasePluginState state = currentState();
    QString workingDirectory = state.currentDirectoryOrTopLevel();
    if (workingDirectory.isEmpty())
        return;
    GitClient::StashGuard stashGuard(workingDirectory, QLatin1String("Revert"));
    if (stashGuard.stashingFailed(true))
        return;
    ChangeSelectionDialog changeSelectionDialog(workingDirectory);
727

Orgad Shaneh's avatar
Orgad Shaneh committed
728
729
730
731
732
    if (changeSelectionDialog.exec() != QDialog::Accepted)
        return;
    const QString change = changeSelectionDialog.change();
    if (!change.isEmpty() && !m_gitClient->revertCommit(workingDirectory, change))
        stashGuard.preventPop();
733
734
}

Orgad Shaneh's avatar
Orgad Shaneh committed
735
void GitPlugin::startCherryPickCommit()
736
737
{
    const VcsBase::VcsBasePluginState state = currentState();
Orgad Shaneh's avatar
Orgad Shaneh committed
738
739
    QString workingDirectory = state.currentDirectoryOrTopLevel();
    if (workingDirectory.isEmpty())
740
        return;
Orgad Shaneh's avatar
Orgad Shaneh committed
741
742
    GitClient::StashGuard stashGuard(state.topLevel(), QLatin1String("Cherry-pick"));
    if (stashGuard.stashingFailed(true))
743
744
745
746
747
748
        return;
    ChangeSelectionDialog changeSelectionDialog(workingDirectory);

    if (changeSelectionDialog.exec() != QDialog::Accepted)
        return;
    const QString change = changeSelectionDialog.change();
Orgad Shaneh's avatar
Orgad Shaneh committed
749
750
    if (!change.isEmpty() && !m_gitClient->cherryPickCommit(workingDirectory, change))
        stashGuard.preventPop();
751
752
}

753
void GitPlugin::stageFile()
con's avatar
con committed
754
{
hjk's avatar
hjk committed
755
    const VcsBase::VcsBasePluginState state = currentState();
756
    QTC_ASSERT(state.hasFile(), return);
757
    m_gitClient->addFile(state.currentFileTopLevel(), state.relativeCurrentFile());
con's avatar
con committed
758
759
}

760
761
void GitPlugin::unstageFile()
{
hjk's avatar
hjk committed
762
    const VcsBase::VcsBasePluginState state = currentState();
763
    QTC_ASSERT(state.hasFile(), return);
764
    m_gitClient->synchronousReset(state.currentFileTopLevel(), QStringList(state.relativeCurrentFile()));
765
766
}

Petar Perisin's avatar
Petar Perisin committed
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
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);
    }
}

804
805
806
807
808
void GitPlugin::startAmendCommit()
{
    startCommit(true);
}

con's avatar
con committed
809
810
void GitPlugin::startCommit()
{
811
812
813
814
815
    startCommit(false);
}

void GitPlugin::startCommit(bool amend)
{
hjk's avatar
hjk committed
816
    if (VcsBase::VcsBaseSubmitEditor::raiseSubmitEditor())
817
        return;
818
    if (isCommitEditorOpen()) {
hjk's avatar
hjk committed
819
        VcsBase::VcsBaseOutputWindow::instance()->appendWarning(tr("Another submit is currently being executed."));
con's avatar
con committed
820
821
822
        return;
    }

hjk's avatar
hjk committed
823
    const VcsBase::VcsBasePluginState state = currentState();
824
    QTC_ASSERT(state.hasTopLevel(), return);
con's avatar
con committed
825
826
827

    QString errorMessage, commitTemplate;
    CommitData data;
828
    if (!m_gitClient->getCommitData(state.topLevel(), amend, &commitTemplate, &data, &errorMessage)) {
hjk's avatar
hjk committed
829
        VcsBase::VcsBaseOutputWindow::instance()->append(errorMessage);
con's avatar
con committed
830
831
832
        return;
    }

833
834
    // Store repository for diff and the original list of
    // files to be able to unstage files the user unchecks
con's avatar
con committed
835
    m_submitRepository = data.panelInfo.repository;
836
    m_commitAmendSHA1 = data.amendSHA1;
con's avatar
con committed
837
838

    // Start new temp file with message template
839
840
841
842
843
    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
844
        VcsBase::VcsBaseOutputWindow::instance()->append(saver.errorString());
con's avatar
con committed
845
846
        return;
    }
847
    m_commitMessageFileName = saver.fileName();
848
    openSubmitEditor(m_commitMessageFileName, data, amend);
con's avatar
con committed
849
850
}

851
Core::IEditor *GitPlugin::openSubmitEditor(const QString &fileName, const CommitData &cd, bool amend)
con's avatar
con committed
852
{
hjk's avatar
hjk committed
853
854
    Core::IEditor *editor = Core::EditorManager::openEditor(fileName, Constants::GITSUBMITEDITOR_ID,
                                                Core::EditorManager::ModeSwitch);
con's avatar
con committed
855
    GitSubmitEditor *submitEditor = qobject_cast<GitSubmitEditor*>(editor);
hjk's avatar
hjk committed
856
    QTC_ASSERT(submitEditor, return 0);
con's avatar
con committed
857
858
    // The actions are for some reason enabled by the context switching
    // mechanism. Disable them correctly.
859
    submitEditor->registerActions(