aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/git/gitclient.h
blob: 398b1c1cc86ff78a91f21ff0d7737105de175e51 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0

#pragma once

#include "gitsettings.h"
#include "git_global.h"
#include "commitdata.h"

#include <coreplugin/editormanager/ieditor.h>
#include <coreplugin/iversioncontrol.h>
#include <vcsbase/vcsbaseclient.h>

#include <utils/fileutils.h>
#include <utils/futuresynchronizer.h>
#include <utils/process.h>

#include <QObject>
#include <QString>
#include <QStringList>
#include <QWidget>

QT_BEGIN_NAMESPACE
class QProcessEnvironment;
class QMenu;
QT_END_NAMESPACE

namespace Core { class ICore; }

namespace DiffEditor {
class ChunkSelection;
class DiffEditorController;
}

namespace VcsBase {
class SubmitFileModel;
class VcsBaseEditorWidget;
class VcsCommand;
}

namespace Git::Internal {

class CommitData;
class GitBaseDiffEditorController;
class GitSubmitEditorPanelData;
class Stash;

enum StatusMode
{
    ShowAll = 0,
    NoUntracked = 1,
    NoSubmodules = 2
};

enum StashFlag {
    Default        = 0x00, /* Prompt and do not allow unstashed */
    AllowUnstashed = 0x01,
    NoPrompt       = 0x02
};

class SubmoduleData
{
public:
    QString dir;
    QString url;
    QString ignore;
};

using SubmoduleDataMap = QMap<QString, SubmoduleData>;

class UpstreamStatus
{
public:
    UpstreamStatus() = default;
    UpstreamStatus(int ahead, int behind) : ahead(ahead), behind(behind) {}

    int ahead = 0;
    int behind = 0;
};

struct Author {
    bool operator==(const Author &other) const {
        return name == other.name && email == other.email;
    }
    bool operator!=(const Author &other) const {
        return !operator==(other);
    }
    QString name;
    QString email;
};

class GITSHARED_EXPORT GitClient : public VcsBase::VcsBaseClientImpl
{
public:
    enum CommandInProgress { NoCommand, Revert, CherryPick,
                             Rebase, Merge, RebaseMerge };
    enum GitKLaunchTrial { Bin, ParentOfBin, SystemPath, None };

    class StashInfo
    {
    public:
        StashInfo() = default;
        enum StashResult { StashUnchanged, StashCanceled, StashFailed,
                           Stashed, NotStashed /* User did not want it */ };

        bool init(const Utils::FilePath &workingDirectory, const QString &command,
                  StashFlag flag = Default, PushAction pushAction = NoPush);
        bool stashingFailed() const;
        void end();
        StashResult result() const { return m_stashResult; }
        QString stashMessage() const { return m_message; }

    private:
        void stashPrompt(const QString &command, const QString &statusOutput, QString *errorMessage);
        void executeStash(const QString &command, QString *errorMessage);

        StashResult m_stashResult = NotStashed;
        QString m_message;
        Utils::FilePath m_workingDir;
        StashFlag m_flags = Default;
        PushAction m_pushAction = NoPush;
    };

    GitClient();
    static GitClient *instance();

    Utils::FilePath vcsBinary() const override;
    QFuture<unsigned> gitVersion() const;

    void vcsExecAbortable(const Utils::FilePath &workingDirectory, const QStringList &arguments,
                          bool isRebase = false, const QString &abortCommand = {},
                          const QObject *context = nullptr,
                          const VcsBase::CommandHandler &handler = {});

    Utils::FilePath findRepositoryForDirectory(const Utils::FilePath &directory) const;
    Utils::FilePath findGitDirForRepository(const Utils::FilePath &repositoryDir) const;
    bool managesFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
    Utils::FilePaths unmanagedFiles(const Utils::FilePaths &filePaths) const;

    void diffFile(const Utils::FilePath &workingDirectory, const QString &fileName) const;
    void diffFiles(const Utils::FilePath &workingDirectory,
                   const QStringList &unstagedFileNames,
                   const QStringList &stagedFileNames) const;
    void diffProject(const Utils::FilePath &workingDirectory,
                     const QString &projectDirectory) const;
    void diffRepository(const Utils::FilePath &workingDirectory) const
    {
        return diffRepository(workingDirectory, {}, {});
    }
    void diffRepository(const Utils::FilePath &workingDirectory,
                        const QString &leftCommit,
                        const QString &rightCommit) const;
    void diffBranch(const Utils::FilePath &workingDirectory,
                    const QString &branchName) const;
    void merge(const Utils::FilePath &workingDirectory, const QStringList &unmergedFileNames = {});

    void status(const Utils::FilePath &workingDirectory) const;
    void log(const Utils::FilePath &workingDirectory, const QString &fileName = {},
             bool enableAnnotationContextMenu = false, const QStringList &args = {});
    void reflog(const Utils::FilePath &workingDirectory, const QString &branch = {});
    void annotate(const Utils::FilePath &workingDir, const QString &file,
                  int lineNumber = -1, const QString &revision = {},
                  const QStringList &extraOptions = {}, int firstLine = -1) override;
    void reset(const Utils::FilePath &workingDirectory, const QString &argument, const QString &commit = {});
    void removeStaleRemoteBranches(const Utils::FilePath &workingDirectory, const QString &remote);
    void recoverDeletedFiles(const Utils::FilePath &workingDirectory);
    void addFile(const Utils::FilePath &workingDirectory, const QString &fileName);
    bool synchronousLog(const Utils::FilePath &workingDirectory, const QStringList &arguments,
                        QString *output, QString *errorMessage = nullptr,
                        VcsBase::RunFlags flags = VcsBase::RunFlags::None);
    bool synchronousAdd(const Utils::FilePath &workingDirectory, const QStringList &files,
                        const QStringList &extraOptions = {});
    bool synchronousDelete(const Utils::FilePath &workingDirectory,
                           bool force,
                           const QStringList &files);
    bool synchronousMove(const Utils::FilePath &workingDirectory,
                         const QString &from,
                         const QString &to);
    bool synchronousReset(const Utils::FilePath &workingDirectory, const QStringList &files = {},
                          QString *errorMessage = nullptr);
    bool synchronousCleanList(const Utils::FilePath &workingDirectory, const QString &modulePath,
                              QStringList *files, QStringList *ignoredFiles, QString *errorMessage);
    bool synchronousApplyPatch(const Utils::FilePath &workingDirectory, const QString &file,
                               QString *errorMessage, const QStringList &extraArguments = {}) const;
    bool synchronousInit(const Utils::FilePath &workingDirectory);
    bool synchronousCheckoutFiles(const Utils::FilePath &workingDirectory, QStringList files = {},
                                  QString revision = {}, QString *errorMessage = nullptr,
                                  bool revertStaging = true);
    enum class StashMode { NoStash, TryStash };
    void checkout(const Utils::FilePath &workingDirectory, const QString &ref,
                  StashMode stashMode = StashMode::TryStash, const QObject *context = nullptr,
                  const VcsBase::CommandHandler &handler = {});

    QStringList setupCheckoutArguments(const Utils::FilePath &workingDirectory, const QString &ref);
    void updateSubmodulesIfNeeded(const Utils::FilePath &workingDirectory, bool prompt);

    // Do a stash and return identier.
    enum { StashPromptDescription = 0x1, StashImmediateRestore = 0x2, StashIgnoreUnchanged = 0x4 };
    QString synchronousStash(const Utils::FilePath &workingDirectory,
                             const QString &messageKeyword = {},
                             unsigned flags = 0, bool *unchanged = nullptr) const;

    bool executeSynchronousStash(const Utils::FilePath &workingDirectory,
                                 const QString &message = {},
                                 bool unstagedOnly = false,
                                 QString *errorMessage = nullptr) const;
    bool synchronousStashRestore(const Utils::FilePath &workingDirectory,
                                 const QString &stash,
                                 bool pop = false,
                                 const QString &branch = {}) const;
    bool synchronousStashRemove(const Utils::FilePath &workingDirectory,
                                const QString &stash = {},
                                QString *errorMessage = nullptr) const;
    bool synchronousBranchCmd(const Utils::FilePath &workingDirectory, QStringList branchArgs,
                              QString *output, QString *errorMessage) const;
    bool synchronousTagCmd(const Utils::FilePath &workingDirectory, QStringList tagArgs,
                           QString *output, QString *errorMessage) const;
    bool synchronousForEachRefCmd(const Utils::FilePath &workingDirectory, QStringList args,
                               QString *output, QString *errorMessage = nullptr) const;
    bool synchronousRemoteCmd(const Utils::FilePath &workingDirectory, QStringList remoteArgs,
                              QString *output = nullptr, QString *errorMessage = nullptr,
                              bool silent = false) const;

    QMap<QString,QString> synchronousRemotesList(const Utils::FilePath &workingDirectory,
                                                 QString *errorMessage = nullptr) const;
    QStringList synchronousSubmoduleStatus(const Utils::FilePath &workingDirectory,
                                           QString *errorMessage = nullptr) const;
    SubmoduleDataMap submoduleList(const Utils::FilePath &workingDirectory) const;
    QByteArray synchronousShow(const Utils::FilePath &workingDirectory, const QString &id,
                               VcsBase::RunFlags flags = VcsBase::RunFlags::None) const;

    bool synchronousRevListCmd(const Utils::FilePath &workingDirectory, const QStringList &extraArguments,
                               QString *output, QString *errorMessage = nullptr) const;

    bool synchronousParentRevisions(const Utils::FilePath &workingDirectory,
                                    const QString &revision,
                                    QStringList *parents,
                                    QString *errorMessage) const;
    QString synchronousShortDescription(const Utils::FilePath &workingDirectory, const QString &revision) const;
    QString synchronousShortDescription(const Utils::FilePath &workingDirectory, const QString &revision,
                                     const QString &format) const;

    QString synchronousCurrentLocalBranch(const Utils::FilePath &workingDirectory) const;

    bool synchronousHeadRefs(const Utils::FilePath &workingDirectory, QStringList *output,
                             QString *errorMessage = nullptr) const;
    QString synchronousTopic(const Utils::FilePath &workingDirectory) const;
    bool synchronousRevParseCmd(const Utils::FilePath &workingDirectory, const QString &ref,
                                QString *output, QString *errorMessage = nullptr) const;
    Tasking::ProcessTask topRevision(const Utils::FilePath &workingDirectory,
        const std::function<void(const QString &, const QDateTime &)> &callback);
    bool isRemoteCommit(const Utils::FilePath &workingDirectory, const QString &commit);

    void fetch(const Utils::FilePath &workingDirectory, const QString &remote);
    void pull(const Utils::FilePath &workingDirectory, bool rebase);
    void push(const Utils::FilePath &workingDirectory, const QStringList &pushArgs = {});
    bool synchronousMerge(const Utils::FilePath &workingDirectory, const QString &branch,
                          bool allowFastForward = true);
    bool canRebase(const Utils::FilePath &workingDirectory) const;
    void rebase(const Utils::FilePath &workingDirectory, const QString &argument);
    void cherryPick(const Utils::FilePath &workingDirectory, const QString &argument);
    void revert(const Utils::FilePath &workingDirectory, const QString &argument);

    bool synchronousRevert(const Utils::FilePath &workingDirectory, const QString &commit);
    bool synchronousCherryPick(const Utils::FilePath &workingDirectory, const QString &commit);
    void interactiveRebase(const Utils::FilePath &workingDirectory, const QString &commit, bool fixup);
    void synchronousAbortCommand(const Utils::FilePath &workingDir, const QString &abortCommand);
    QString synchronousTrackingBranch(const Utils::FilePath &workingDirectory,
                                      const QString &branch = {});
    bool synchronousSetTrackingBranch(const Utils::FilePath &workingDirectory,
                                      const QString &branch,
                                      const QString &tracking);

    // git svn support (asynchronous).
    void synchronousSubversionFetch(const Utils::FilePath &workingDirectory) const;
    void subversionLog(const Utils::FilePath &workingDirectory) const;
    void subversionDeltaCommit(const Utils::FilePath &workingDirectory) const;

    void stashPop(const Utils::FilePath &workingDirectory, const QString &stash = {});
    void revertFiles(const QStringList &files, bool revertStaging);
    bool synchronousStashList(const Utils::FilePath &workingDirectory, QList<Stash> *stashes,
                              QString *errorMessage = nullptr) const;
    // Resolve a stash name from message (for IVersionControl's names).
    bool stashNameFromMessage(const Utils::FilePath &workingDirectory, const QString &messge, QString *name,
                              QString *errorMessage = nullptr) const;

    QString readGitVar(const Utils::FilePath &workingDirectory, const QString &configVar) const;
    QString readConfigValue(const Utils::FilePath &workingDirectory, const QString &configVar) const;
    QChar commentChar(const Utils::FilePath &workingDirectory);
    void setConfigValue(const Utils::FilePath &workingDirectory, const QString &configVar,
                        const QString &value) const;

    bool readDataFromCommit(const Utils::FilePath &repoDirectory, const QString &commit,
                            CommitData &commitData, QString *errorMessage = nullptr,
                            QString *commitTemplate = nullptr);
    bool getCommitData(const Utils::FilePath &workingDirectory, QString *commitTemplate,
                       CommitData &commitData, QString *errorMessage);

    bool addAndCommit(const Utils::FilePath &workingDirectory,
                      const GitSubmitEditorPanelData &data,
                      CommitType commitType,
                      const QString &amendSHA1,
                      const QString &messageFile,
                      VcsBase::SubmitFileModel *model);

    enum StatusResult { StatusChanged, StatusUnchanged, StatusFailed };
    StatusResult gitStatus(const Utils::FilePath &workingDirectory, StatusMode mode,
                           QString *output = nullptr, QString *errorMessage = nullptr) const;

    CommandInProgress checkCommandInProgress(const Utils::FilePath &workingDirectory) const;
    QString commandInProgressDescription(const Utils::FilePath &workingDirectory) const;

    void continueCommandIfNeeded(const Utils::FilePath &workingDirectory, bool allowContinue = true);

    void launchGitK(const Utils::FilePath &workingDirectory, const QString &fileName) const;
    void launchGitK(const Utils::FilePath &workingDirectory) const { launchGitK(workingDirectory, QString()); }
    bool launchGitGui(const Utils::FilePath &workingDirectory);
    Utils::FilePath gitBinDirectory() const;
    bool launchGitBash(const Utils::FilePath &workingDirectory);

    void launchRepositoryBrowser(const Utils::FilePath &workingDirectory) const;

    QStringList synchronousRepositoryBranches(const QString &repositoryURL,
                                              const Utils::FilePath &workingDirectory = {}) const;

    Utils::Environment processEnvironment() const override;

    bool beginStashScope(const Utils::FilePath &workingDirectory, const QString &command,
                         StashFlag flag = Default, PushAction pushAction = NoPush);
    StashInfo &stashInfo(const Utils::FilePath &workingDirectory);
    void endStashScope(const Utils::FilePath &workingDirectory);
    bool isValidRevision(const QString &revision) const;
    void handleMergeConflicts(const Utils::FilePath &workingDir, const QString &commit,
                              const QStringList &files, const QString &abortCommand);
    void addFuture(const QFuture<void> &future);

    static QString msgNoChangedFiles();
    static QString msgNoCommits(bool includeRemote);
    void show(const Utils::FilePath &source, const QString &id, const QString &name = {});
    void archive(const Utils::FilePath &workingDirectory, QString commit);

    enum class BranchTargetType { Remote, Commit };
    static QString suggestedLocalBranchName(
            const Utils::FilePath &workingDirectory, const QStringList &existingLocalNames,
            const QString &target, BranchTargetType targetType);
    static void addChangeActions(QMenu *menu, const Utils::FilePath &source, const QString &change);
    static Utils::FilePath fileWorkingDirectory(const Utils::FilePath &file);
    enum class ShowEditor { OnlyIfDifferent, Always };
    Core::IEditor *openShowEditor(const Utils::FilePath &workingDirectory, const QString &ref,
                                  const Utils::FilePath &path, ShowEditor showSetting = ShowEditor::Always);

    Author parseAuthor(const QString &authorInfo);
    Author getAuthor(const Utils::FilePath &workingDirectory);

    QTextCodec *defaultCommitEncoding() const;
    enum EncodingType { EncodingSource, EncodingLogOutput, EncodingCommit, EncodingDefault };
    QTextCodec *encoding(EncodingType encodingType, const Utils::FilePath &source = {}) const;

    void readConfigAsync(const Utils::FilePath &workingDirectory, const QStringList &arguments,
                         const VcsBase::CommandHandler &handler) const;

private:
    static GitSettings &settings();

    void finishSubmoduleUpdate();
    void chunkActionsRequested(DiffEditor::DiffEditorController *controller,
                               QMenu *menu, int fileIndex, int chunkIndex,
                               const DiffEditor::ChunkSelection &selection) const;

    void stage(DiffEditor::DiffEditorController *diffController,
               const QString &patch, bool revert) const;

    void requestReload(const QString &documentId, const Utils::FilePath &source,
                       const QString &title, const Utils::FilePath &workingDirectory,
                       std::function<GitBaseDiffEditorController *(Core::IDocument *)> factory) const;

    QString readOneLine(const Utils::FilePath &workingDirectory, const QStringList &arguments) const;

    enum RevertResult { RevertOk, RevertUnchanged, RevertCanceled, RevertFailed };
    RevertResult revertI(QStringList files,
                         bool *isDirectory,
                         QString *errorMessage,
                         bool revertStaging);
    bool executeAndHandleConflicts(const Utils::FilePath &workingDirectory, const QStringList &arguments,
                                   const QString &abortCommand = {}) const;
    void tryLaunchingGitK(const Utils::Environment &env,
                          const Utils::FilePath &workingDirectory,
                          const QString &fileName,
                          GitKLaunchTrial trial = GitKLaunchTrial::Bin) const;
    void handleGitKFailedToStart(const Utils::Environment &env,
                                 const Utils::FilePath &workingDirectory,
                                 const QString &fileName,
                                 const GitKLaunchTrial oldTrial,
                                 const Utils::FilePath &oldGitBinDir) const;
    bool cleanList(const Utils::FilePath &workingDirectory, const QString &modulePath,
                   const QString &flag, QStringList *files, QString *errorMessage);

    enum ContinueCommandMode {
        ContinueOnly,
        SkipOnly,
        SkipIfNoChanges
    };

    void continuePreviousGitCommand(const Utils::FilePath &workingDirectory, const QString &msgBoxTitle,
                                    QString msgBoxText, const QString &buttonName,
                                    const QString &gitCommand, ContinueCommandMode continueMode);

    mutable Utils::FilePath m_gitVersionForBinary;
    mutable unsigned m_cachedGitVersion = 0;

    QString m_gitQtcEditor;
    QMap<Utils::FilePath, StashInfo> m_stashInfo;
    QString m_diffCommit;
    Utils::FilePaths m_updatedSubmodules;
    bool m_disableEditor = false;
    // The synchronizer has cancelOnWait set to true by default.
    Utils::FutureSynchronizer m_synchronizer; // for commit updates
};

class GitRemote : public Core::IVersionControl::RepoUrl
{
public:
    GitRemote(const QString &location);
};

} // Git::Internal