1 /*
2     SPDX-FileCopyrightText: 2008 Evgeniy Ivanov <powerfox@kde.ru>
3     SPDX-FileCopyrightText: 2009 Hugo Parente Lima <hugo.pl@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
6 */
7 
8 #ifndef KDEVPLATFORM_PLUGIN_GIT_PLUGIN_H
9 #define KDEVPLATFORM_PLUGIN_GIT_PLUGIN_H
10 
11 #include <vcs/interfaces/idistributedversioncontrol.h>
12 #include <vcs/interfaces/icontentawareversioncontrol.h>
13 #include <vcs/dvcs/dvcsplugin.h>
14 #include <vcs/vcsstatusinfo.h>
15 #include <outputview/outputjob.h>
16 #include <vcs/vcsjob.h>
17 
18 class KDirWatch;
19 class QDir;
20 
21 class RepoStatusModel;
22 class CommitToolViewFactory;
23 
24 namespace KDevelop
25 {
26     class VcsJob;
27     class VcsRevision;
28 }
29 
30 class StandardJob : public KDevelop::VcsJob
31 {
32     Q_OBJECT
33     public:
34         StandardJob(KDevelop::IPlugin* parent, KJob* job, OutputJobVerbosity verbosity);
35 
fetchResults()36         QVariant fetchResults() override { return QVariant(); }
37         void start() override;
status()38         JobStatus status() const override { return m_status; }
vcsPlugin()39         KDevelop::IPlugin* vcsPlugin() const override { return m_plugin; }
40 
41     public Q_SLOTS:
42         void result(KJob*);
43 
44     private:
45         KJob* m_job;
46         KDevelop::IPlugin* m_plugin;
47         JobStatus m_status;
48 };
49 
50 /**
51  * This is the main class of KDevelop's Git plugin.
52  *
53  * It implements the DVCS dependent things not implemented in KDevelop::DistributedVersionControlPlugin
54  * @author Evgeniy Ivanov <powerfox@kde.ru>
55  */
56 class GitPlugin: public KDevelop::DistributedVersionControlPlugin, public KDevelop::IContentAwareVersionControl
57 {
58     Q_OBJECT
59     Q_INTERFACES(KDevelop::IBasicVersionControl KDevelop::IDistributedVersionControl KDevelop::IContentAwareVersionControl)
60     friend class GitInitTest;
61 public:
62 
63     enum ExtendedState {
64         /* Unchanged in index (no staged changes) */
65         GitXX = KDevelop::VcsStatusInfo::ItemUserState, // No changes in worktree
66 
67                // Changed in worktree, not staged for commit
68         GitXM, // Modified in worktree
69         GitXD, // Deleted in worktree
70         GitXR, // Renamed in worktree
71         GitXC, // Copied in worktree
72 
73         /* Changes in index (staged changes) */
74         GitMX, // No changes in worktree
75                   // Changed in worktree, not staged for commit
76         GitMM, // Modified in worktree
77         GitMD, // Deleted in worktree
78 
79         /* Added to index (new item) */
80         GitAX, // No changes in worktree
81                   // Changes in worktree, not staged for commit
82         GitAM, // Modified in worktree
83         GitAD, // Deleted in worktree
84 
85         /* Deleted from index */
86         GitDX, // No changes in worktree (deleted in wt)
87         GitDR, // Renamed in worktree
88         GitDC, // Copied in worktree
89 
90         /* Renamed in index */
91         GitRX, // No changes in worktree
92         GitRM, // Modified in worktree
93         GitRD, // Deleted in worktree
94 
95         /* Copied in index */
96         GitCX, // No changes in worktree
97         GitCM, // Modified in worktree
98         GitCD, // Deleted in worktree
99 
100         /* Special states */
101         GitUntracked, // ? ? --- untracked files
102         GitConflicts, // U, AA, DD --- conflicts
103         GitInvalid = -1, // not really a state
104     };
105 
106     /**
107      * Enums with values which are used as function arguments
108      * instead of bools for better readability.
109      *
110      * The enums are named ${function_name}Params.
111      */
112     enum ApplyParams {
113         Index = 0,
114         WorkTree = 2,
115     };
116 
117 
118     explicit GitPlugin(QObject *parent, const QVariantList & args = QVariantList() );
119     ~GitPlugin() override;
120 
121     QString name() const override;
122 
123     bool isValidRemoteRepositoryUrl(const QUrl& remoteLocation) override;
124     bool isVersionControlled(const QUrl &path) override;
125 
126     KDevelop::VcsJob* copy(const QUrl& localLocationSrc, const QUrl& localLocationDstn) override;
127     KDevelop::VcsJob* move(const QUrl& localLocationSrc, const QUrl& localLocationDst) override;
128 
129     //TODO
130     KDevelop::VcsJob* pull(const KDevelop::VcsLocation& localOrRepoLocationSrc, const QUrl& localRepositoryLocation) override;
131     KDevelop::VcsJob* push(const QUrl& localRepositoryLocation, const KDevelop::VcsLocation& localOrRepoLocationDst) override;
132     KDevelop::VcsJob* repositoryLocation(const QUrl& localLocation) override;
133     KDevelop::VcsJob* resolve(const QList<QUrl>& localLocations, RecursionMode recursion) override;
134     KDevelop::VcsJob* update(const QList<QUrl>& localLocations, const KDevelop::VcsRevision& rev, RecursionMode recursion) override;
135     KDevelop::VcsLocationWidget* vcsLocation(QWidget* parent) const override;
136     void setupCommitMessageEditor(const QUrl& localLocation, KTextEdit* editor) const override;
137     //End of
138 
139     KDevelop::VcsJob* add(const QList<QUrl>& localLocations,
140                           KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override;
141     KDevelop::VcsJob* createWorkingCopy(const KDevelop::VcsLocation & localOrRepoLocationSrc,
142                             const QUrl& localRepositoryRoot, KDevelop::IBasicVersionControl::RecursionMode) override;
143 
144     KDevelop::VcsJob* remove(const QList<QUrl>& files) override;
145     KDevelop::VcsJob* status(const QList<QUrl>& localLocations,
146                              KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override;
147     KDevelop::VcsJob* commit(const QString& message,
148                              const QList<QUrl>& localLocations,
149                              KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override;
150 
151     /**
152      * Commits staged changes to the repo located at repoUrl.
153      *
154      * @param message the commit message
155      * @param repoUrl the url pointing to the repo directory (or a file in the repo)
156      */
157     KDevelop::VcsJob* commitStaged(const QString& message, const QUrl& repoUrl);
158 
159     KDevelop::VcsJob* diff(const QUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision,
160                                    RecursionMode recursion) override;
161     /**
162      * Shows a diff of changes between srcRevision and dstRevision.
163      *
164      * @param repoPath a path pointing somewhere inside the repo
165      * @param srcRevision the source revision
166      * @param dstRevision the destination revision
167      *
168      * @note: This differs from the @ref:diff method in @ref:IBasicVersionControl in that it does not require
169      * a list of files but automatically shows all changed files
170      */
171     KDevelop::VcsJob* diff(const QUrl& repoPath, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision);
172 
173     KDevelop::VcsJob* log( const QUrl& localLocation, const KDevelop::VcsRevision& rev, unsigned long limit) override;
174     KDevelop::VcsJob* log(const QUrl& localLocation, const KDevelop::VcsRevision& rev, const KDevelop::VcsRevision& limit) override;
175     KDevelop::VcsJob* annotate(const QUrl &localLocation, const KDevelop::VcsRevision &rev) override;
176     KDevelop::VcsJob* revert(const QList<QUrl>& localLocations, RecursionMode recursion) override;
177 
178     /**
179      * Resets all changes in the specified files which were "staged for commit".
180      *
181      * @param localLocations the local files/dirs changes to which should be reset
182      * @param recursion defines whether changes should be reset recursively in all files
183      * in a directory, if localLocations contain a directory
184      */
185     KDevelop::VcsJob* reset(const QList<QUrl>& localLocations, RecursionMode recursion);
186 
187     /**
188      * Applies the patch given by a diff to the repo
189      *
190      * @param diff the patch
191      * @param applyTo where to apply the patch (index or worktree)
192      */
193     KDevelop::VcsJob* apply(const KDevelop::VcsDiff& diff, ApplyParams applyTo = WorkTree);
194 
195     // Begin:  KDevelop::IDistributedVersionControl
196     KDevelop::VcsJob* init(const QUrl & directory) override;
197 
198     // Branch management
199     KDevelop::VcsJob* tag(const QUrl& repository, const QString& commitMessage, const KDevelop::VcsRevision& rev, const QString& tagName) override;
200     KDevelop::VcsJob* branch(const QUrl& repository, const KDevelop::VcsRevision& rev, const QString& branchName) override;
201     KDevelop::VcsJob* branches(const QUrl& repository) override;
202     KDevelop::VcsJob* currentBranch(const QUrl& repository) override;
203     KDevelop::VcsJob* deleteBranch(const QUrl& repository, const QString& branchName) override;
204     KDevelop::VcsJob* switchBranch(const QUrl& repository, const QString& branchName) override;
205     KDevelop::VcsJob* renameBranch(const QUrl& repository, const QString& oldBranchName, const QString& newBranchName) override;
206     KDevelop::VcsJob* mergeBranch(const QUrl& repository, const QString& branchName) override;
207     KDevelop::VcsJob* rebase(const QUrl& repository, const QString& branchName);
208 
209     //graph helpers
210     QVector<KDevelop::DVcsEvent> allCommits(const QString& repo) override;
211 
212     //used in log
213     void parseLogOutput(const KDevelop::DVcsJob* job,
214                         QVector<KDevelop::DVcsEvent>& commits) const override;
215 
216     void additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls) override;
217 
218     KDevelop::DVcsJob* gitStash(const QDir& repository, const QStringList& args, KDevelop::OutputJob::OutputJobVerbosity verbosity);
219 
220     bool hasStashes(const QDir& repository);
221     bool hasModifications(const QDir& repository);
222     bool hasModifications(const QDir& repo, const QUrl& file);
223 
224     void registerRepositoryForCurrentBranchChanges(const QUrl& repository) override;
225 
226     KDevelop::CheckInRepositoryJob* isInRepository(KTextEditor::Document* document) override;
227 
228     KDevelop::DVcsJob* setConfigOption(const QUrl& repository, const QString& key, const QString& value, bool global = false);
229     QString readConfigOption(const QUrl& repository, const QString& key);
230 
231     // this indicates whether the diff() function will generate a diff (patch) which
232     // includes the working copy directory name or not (in which case git diff is called
233     // with --no-prefix).
usePrefix()234     bool usePrefix() const
235     {
236         return m_usePrefix;
237     }
238 
setUsePrefix(bool p)239     void setUsePrefix(bool p)
240     {
241         m_usePrefix = p;
242     }
243 protected:
244 
245     QUrl repositoryRoot(const QUrl& path);
246 
247     bool isValidDirectory(const QUrl &dirPath) override;
248 
249     KDevelop::DVcsJob* lsFiles(const QDir &repository,
250                      const QStringList &args,
251                      KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Verbose);
252     KDevelop::DVcsJob* gitRevList(const QString &directory,
253                         const QStringList &args);
254     KDevelop::DVcsJob* gitRevParse(const QString &repository,
255                          const QStringList &args,
256                          KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Silent);
257 
258 private Q_SLOTS:
259     void parseGitBlameOutput(KDevelop::DVcsJob *job);
260     void parseGitLogOutput(KDevelop::DVcsJob *job);
261     void parseGitDiffOutput(KDevelop::DVcsJob* job);
262     void parseGitRepoLocationOutput(KDevelop::DVcsJob* job);
263     void parseGitStatusOutput(KDevelop::DVcsJob* job);
264     void parseGitStatusOutput_old(KDevelop::DVcsJob* job);
265     void parseGitVersionOutput(KDevelop::DVcsJob* job);
266     void parseGitBranchOutput(KDevelop::DVcsJob* job);
267     void parseGitCurrentBranch(KDevelop::DVcsJob* job);
268 
269     void ctxRebase();
270     void ctxPushStash();
271     void ctxPopStash();
272     void ctxStashManager();
273 
274     void fileChanged(const QString& file);
275     void delayedBranchChanged();
276 
277 Q_SIGNALS:
278     void repositoryBranchChanged(const QUrl& repository);
279 
280 private:
281     bool ensureValidGitIdentity(const QDir& dir);
282     void addNotVersionedFiles(const QDir& dir, const QList<QUrl>& files);
283 
284     //commit dialog "main" helper
285     QStringList getLsFiles(const QDir &directory, const QStringList &args,
286         KDevelop::OutputJob::OutputJobVerbosity verbosity);
287     KDevelop::DVcsJob* errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity);
288 
289     void initBranchHash(const QString &repo);
290 
291     /**
292      * Parses a git status --porcelain line
293      *
294      * @param statusLine a line as returned by `git status --porcelain`
295      * @returns the appropriate extended status
296      */
297     static ExtendedState parseGitState(const QStringRef& statusLine);
298 
299     /**
300      * Maps an extended state to a basic state
301      *
302      * @param state the extended state as provided by git (i.e. describing the combined status in the index & worktree)
303      */
304     static KDevelop::VcsStatusInfo::State extendedStateToBasic(const ExtendedState state);
305 
306     QList<QStringList> branchesShas;
307     QList<QUrl> m_urls;
308 
309     /** Tells if it's older than 1.7.0 or not */
310     bool m_oldVersion = false;
311 
312     KDirWatch* m_watcher;
313     QList<QUrl> m_branchesChange;
314     bool m_usePrefix = true;
315 
316     /** A tree model tracking and classifying changes into staged, unstaged and untracked */
317     RepoStatusModel* m_repoStatusModel;
318 
319     /** A factory for constructing the tool view for preparing commits */
320     CommitToolViewFactory* m_commitToolViewFactory;
321 };
322 
323 QVariant runSynchronously(KDevelop::VcsJob* job);
324 
325 #endif
326