File indexing completed on 2024-04-28 04:38:51
0001 /* 0002 SPDX-FileCopyrightText: 2008 Evgeniy Ivanov <powerfox@kde.ru> 0003 SPDX-FileCopyrightText: 2009 Hugo Parente Lima <hugo.pl@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0006 */ 0007 0008 #ifndef KDEVPLATFORM_PLUGIN_GIT_PLUGIN_H 0009 #define KDEVPLATFORM_PLUGIN_GIT_PLUGIN_H 0010 0011 #include <vcs/interfaces/idistributedversioncontrol.h> 0012 #include <vcs/interfaces/icontentawareversioncontrol.h> 0013 #include <vcs/dvcs/dvcsplugin.h> 0014 #include <vcs/vcsstatusinfo.h> 0015 #include <outputview/outputjob.h> 0016 #include <vcs/vcsjob.h> 0017 0018 #include <QDateTime> 0019 0020 class KDirWatch; 0021 class QDir; 0022 0023 class RepoStatusModel; 0024 class CommitToolViewFactory; 0025 0026 namespace KDevelop 0027 { 0028 class VcsJob; 0029 class VcsRevision; 0030 } 0031 0032 class StandardJob : public KDevelop::VcsJob 0033 { 0034 Q_OBJECT 0035 public: 0036 StandardJob(KDevelop::IPlugin* parent, KJob* job, OutputJobVerbosity verbosity); 0037 0038 QVariant fetchResults() override { return QVariant(); } 0039 void start() override; 0040 JobStatus status() const override { return m_status; } 0041 KDevelop::IPlugin* vcsPlugin() const override { return m_plugin; } 0042 0043 public Q_SLOTS: 0044 void result(KJob*); 0045 0046 private: 0047 KJob* m_job; 0048 KDevelop::IPlugin* m_plugin; 0049 JobStatus m_status; 0050 }; 0051 0052 /** 0053 * This is the main class of KDevelop's Git plugin. 0054 * 0055 * It implements the DVCS dependent things not implemented in KDevelop::DistributedVersionControlPlugin 0056 * @author Evgeniy Ivanov <powerfox@kde.ru> 0057 */ 0058 class GitPlugin: public KDevelop::DistributedVersionControlPlugin, public KDevelop::IContentAwareVersionControl 0059 { 0060 Q_OBJECT 0061 Q_INTERFACES(KDevelop::IBasicVersionControl KDevelop::IDistributedVersionControl KDevelop::IContentAwareVersionControl) 0062 friend class GitInitTest; 0063 public: 0064 0065 enum ExtendedState { 0066 /* Unchanged in index (no staged changes) */ 0067 GitXX = KDevelop::VcsStatusInfo::ItemUserState, // No changes in worktree 0068 0069 // Changed in worktree, not staged for commit 0070 GitXM, // Modified in worktree 0071 GitXD, // Deleted in worktree 0072 GitXR, // Renamed in worktree 0073 GitXC, // Copied in worktree 0074 0075 /* Changes in index (staged changes) */ 0076 GitMX, // No changes in worktree 0077 // Changed in worktree, not staged for commit 0078 GitMM, // Modified in worktree 0079 GitMD, // Deleted in worktree 0080 0081 /* Added to index (new item) */ 0082 GitAX, // No changes in worktree 0083 // Changes in worktree, not staged for commit 0084 GitAM, // Modified in worktree 0085 GitAD, // Deleted in worktree 0086 0087 /* Deleted from index */ 0088 GitDX, // No changes in worktree (deleted in wt) 0089 GitDR, // Renamed in worktree 0090 GitDC, // Copied in worktree 0091 0092 /* Renamed in index */ 0093 GitRX, // No changes in worktree 0094 GitRM, // Modified in worktree 0095 GitRD, // Deleted in worktree 0096 0097 /* Copied in index */ 0098 GitCX, // No changes in worktree 0099 GitCM, // Modified in worktree 0100 GitCD, // Deleted in worktree 0101 0102 /* Special states */ 0103 GitUntracked, // ? ? --- untracked files 0104 GitConflicts, // U, AA, DD --- conflicts 0105 GitInvalid = -1, // not really a state 0106 }; 0107 0108 /** 0109 * Enums with values which are used as function arguments 0110 * instead of bools for better readability. 0111 * 0112 * The enums are named ${function_name}Params. 0113 */ 0114 enum ApplyParams { 0115 Index = 0, 0116 WorkTree = 2, 0117 }; 0118 0119 0120 explicit GitPlugin(QObject *parent, const QVariantList & args = QVariantList() ); 0121 ~GitPlugin() override; 0122 0123 QString name() const override; 0124 0125 bool isValidRemoteRepositoryUrl(const QUrl& remoteLocation) override; 0126 bool isVersionControlled(const QUrl &path) override; 0127 0128 KDevelop::VcsJob* copy(const QUrl& localLocationSrc, const QUrl& localLocationDstn) override; 0129 KDevelop::VcsJob* move(const QUrl& localLocationSrc, const QUrl& localLocationDst) override; 0130 0131 //TODO 0132 KDevelop::VcsJob* pull(const KDevelop::VcsLocation& localOrRepoLocationSrc, const QUrl& localRepositoryLocation) override; 0133 KDevelop::VcsJob* push(const QUrl& localRepositoryLocation, const KDevelop::VcsLocation& localOrRepoLocationDst) override; 0134 KDevelop::VcsJob* repositoryLocation(const QUrl& localLocation) override; 0135 KDevelop::VcsJob* resolve(const QList<QUrl>& localLocations, RecursionMode recursion) override; 0136 KDevelop::VcsJob* update(const QList<QUrl>& localLocations, const KDevelop::VcsRevision& rev, RecursionMode recursion) override; 0137 KDevelop::VcsLocationWidget* vcsLocation(QWidget* parent) const override; 0138 void setupCommitMessageEditor(const QUrl& localLocation, KTextEdit* editor) const override; 0139 //End of 0140 0141 KDevelop::VcsJob* add(const QList<QUrl>& localLocations, 0142 KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override; 0143 KDevelop::VcsJob* createWorkingCopy(const KDevelop::VcsLocation & localOrRepoLocationSrc, 0144 const QUrl& localRepositoryRoot, KDevelop::IBasicVersionControl::RecursionMode) override; 0145 0146 KDevelop::VcsJob* remove(const QList<QUrl>& files) override; 0147 KDevelop::VcsJob* status(const QList<QUrl>& localLocations, 0148 KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override; 0149 KDevelop::VcsJob* commit(const QString& message, 0150 const QList<QUrl>& localLocations, 0151 KDevelop::IBasicVersionControl::RecursionMode recursion = KDevelop::IBasicVersionControl::Recursive) override; 0152 0153 /** 0154 * Commits staged changes to the repo located at repoUrl. 0155 * 0156 * @param message the commit message 0157 * @param repoUrl the url pointing to the repo directory (or a file in the repo) 0158 */ 0159 KDevelop::VcsJob* commitStaged(const QString& message, const QUrl& repoUrl); 0160 0161 KDevelop::VcsJob* diff(const QUrl& fileOrDirectory, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision, 0162 RecursionMode recursion) override; 0163 /** 0164 * Shows a diff of changes between srcRevision and dstRevision. 0165 * 0166 * @param repoPath a path pointing somewhere inside the repo 0167 * @param srcRevision the source revision 0168 * @param dstRevision the destination revision 0169 * 0170 * @note: This differs from the @ref:diff method in @ref:IBasicVersionControl in that it does not require 0171 * a list of files but automatically shows all changed files 0172 */ 0173 KDevelop::VcsJob* diff(const QUrl& repoPath, const KDevelop::VcsRevision& srcRevision, const KDevelop::VcsRevision& dstRevision); 0174 0175 KDevelop::VcsJob* log( const QUrl& localLocation, const KDevelop::VcsRevision& rev, unsigned long limit) override; 0176 KDevelop::VcsJob* log(const QUrl& localLocation, const KDevelop::VcsRevision& rev, const KDevelop::VcsRevision& limit) override; 0177 KDevelop::VcsJob* annotate(const QUrl &localLocation, const KDevelop::VcsRevision &rev) override; 0178 KDevelop::VcsJob* revert(const QList<QUrl>& localLocations, RecursionMode recursion) override; 0179 0180 /** 0181 * Resets all changes in the specified files which were "staged for commit". 0182 * 0183 * @param localLocations the local files/dirs changes to which should be reset 0184 * @param recursion defines whether changes should be reset recursively in all files 0185 * in a directory, if localLocations contain a directory 0186 */ 0187 KDevelop::VcsJob* reset(const QList<QUrl>& localLocations, RecursionMode recursion); 0188 0189 /** 0190 * Applies the patch given by a diff to the repo 0191 * 0192 * @param diff the patch 0193 * @param applyTo where to apply the patch (index or worktree) 0194 */ 0195 KDevelop::VcsJob* apply(const KDevelop::VcsDiff& diff, ApplyParams applyTo = WorkTree); 0196 0197 // Begin: KDevelop::IDistributedVersionControl 0198 KDevelop::VcsJob* init(const QUrl & directory) override; 0199 0200 // Branch management 0201 KDevelop::VcsJob* tag(const QUrl& repository, const QString& commitMessage, const KDevelop::VcsRevision& rev, const QString& tagName) override; 0202 KDevelop::VcsJob* branch(const QUrl& repository, const KDevelop::VcsRevision& rev, const QString& branchName) override; 0203 KDevelop::VcsJob* branches(const QUrl& repository) override; 0204 KDevelop::VcsJob* currentBranch(const QUrl& repository) override; 0205 KDevelop::VcsJob* deleteBranch(const QUrl& repository, const QString& branchName) override; 0206 KDevelop::VcsJob* switchBranch(const QUrl& repository, const QString& branchName) override; 0207 KDevelop::VcsJob* renameBranch(const QUrl& repository, const QString& oldBranchName, const QString& newBranchName) override; 0208 KDevelop::VcsJob* mergeBranch(const QUrl& repository, const QString& branchName) override; 0209 KDevelop::VcsJob* rebase(const QUrl& repository, const QString& branchName); 0210 0211 //graph helpers 0212 QVector<KDevelop::DVcsEvent> allCommits(const QString& repo) override; 0213 0214 //used in log 0215 void parseLogOutput(const KDevelop::DVcsJob* job, 0216 QVector<KDevelop::DVcsEvent>& commits) const override; 0217 0218 void additionalMenuEntries(QMenu* menu, const QList<QUrl>& urls) override; 0219 0220 // Stash Management 0221 0222 /** 0223 * Structure to hold information about an item on the stash stack 0224 */ 0225 struct StashItem { 0226 int stackDepth = -1; /* Position on the stack */ 0227 QString shortRef; /* The reflog selector (e.g. stash@{0}) */ 0228 QString parentSHA; /* The short SHA of the commit on which the stash was made */ 0229 QString parentDescription; /* A short description of the commit on which the stash was made */ 0230 QString branch; /* The branch on which the stash was made */ 0231 QString message; /* The message with which the stash was made */ 0232 QDateTime creationTime; /* The date-time the stash item was committed */ 0233 }; 0234 0235 /** 0236 * Returns a job to run `git stash` in the repository @p repository with 0237 * additional arguments @p args. 0238 * 0239 * The @p verbosity parameter will determine whether the job output will 0240 * be shown in the VCS Output ToolView. 0241 * 0242 * For example, a job to silently apply the top-most stashed item to the current 0243 * tree would be created as follows: 0244 * 0245 * gitStash(repoDir, {QStringLiteral("apply")}, KDevelop::OutputJob::Silent) 0246 * 0247 */ 0248 KDevelop::VcsJob* gitStash(const QDir& repository, const QStringList& args, KDevelop::OutputJob::OutputJobVerbosity verbosity); 0249 0250 /** 0251 * The result (job->fetchResults()) will be a @ref QList of @ref StashItem s 0252 * 0253 * @p repository is the repository to work on 0254 * @p verbosity determines whether the job output will be shown in the VCS Output ToolView 0255 */ 0256 KDevelop::VcsJob* stashList(const QDir& repository, KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Silent); 0257 0258 bool hasStashes(const QDir& repository); 0259 bool hasModifications(const QDir& repository); 0260 bool hasModifications(const QDir& repo, const QUrl& file); 0261 0262 void registerRepositoryForCurrentBranchChanges(const QUrl& repository) override; 0263 0264 KDevelop::CheckInRepositoryJob* isInRepository(KTextEditor::Document* document) override; 0265 0266 KDevelop::DVcsJob* setConfigOption(const QUrl& repository, const QString& key, const QString& value, bool global = false); 0267 QString readConfigOption(const QUrl& repository, const QString& key); 0268 0269 // this indicates whether the diff() function will generate a diff (patch) which 0270 // includes the working copy directory name or not (in which case git diff is called 0271 // with --no-prefix). 0272 bool usePrefix() const 0273 { 0274 return m_usePrefix; 0275 } 0276 0277 void setUsePrefix(bool p) 0278 { 0279 m_usePrefix = p; 0280 } 0281 protected: 0282 0283 QUrl repositoryRoot(const QUrl& path); 0284 0285 bool isValidDirectory(const QUrl &dirPath) override; 0286 0287 KDevelop::DVcsJob* lsFiles(const QDir &repository, 0288 const QStringList &args, 0289 KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Verbose); 0290 KDevelop::DVcsJob* gitRevList(const QString &directory, 0291 const QStringList &args); 0292 KDevelop::DVcsJob* gitRevParse(const QString &repository, 0293 const QStringList &args, 0294 KDevelop::OutputJob::OutputJobVerbosity verbosity = KDevelop::OutputJob::Silent); 0295 0296 private Q_SLOTS: 0297 void parseGitBlameOutput(KDevelop::DVcsJob *job); 0298 void parseGitLogOutput(KDevelop::DVcsJob *job); 0299 void parseGitDiffOutput(KDevelop::DVcsJob* job); 0300 void parseGitRepoLocationOutput(KDevelop::DVcsJob* job); 0301 void parseGitStatusOutput(KDevelop::DVcsJob* job); 0302 void parseGitStatusOutput_old(KDevelop::DVcsJob* job); 0303 void parseGitVersionOutput(KDevelop::DVcsJob* job); 0304 void parseGitBranchOutput(KDevelop::DVcsJob* job); 0305 void parseGitCurrentBranch(KDevelop::DVcsJob* job); 0306 void parseGitStashList(KDevelop::VcsJob* job); 0307 0308 void ctxRebase(); 0309 void ctxPushStash(); 0310 void ctxPopStash(); 0311 void ctxStashManager(); 0312 0313 void fileChanged(const QString& file); 0314 void delayedBranchChanged(); 0315 0316 Q_SIGNALS: 0317 void repositoryBranchChanged(const QUrl& repository); 0318 0319 private: 0320 bool ensureValidGitIdentity(const QDir& dir); 0321 void addNotVersionedFiles(const QDir& dir, const QList<QUrl>& files); 0322 0323 //commit dialog "main" helper 0324 QStringList getLsFiles(const QDir &directory, const QStringList &args, 0325 KDevelop::OutputJob::OutputJobVerbosity verbosity); 0326 KDevelop::DVcsJob* errorsFound(const QString& error, KDevelop::OutputJob::OutputJobVerbosity verbosity); 0327 0328 void initBranchHash(const QString &repo); 0329 0330 /** 0331 * Parses a git status --porcelain line 0332 * 0333 * @param statusLine a line as returned by `git status --porcelain` 0334 * @returns the appropriate extended status 0335 */ 0336 static ExtendedState parseGitState(const QStringRef& statusLine); 0337 0338 /** 0339 * Maps an extended state to a basic state 0340 * 0341 * @param state the extended state as provided by git (i.e. describing the combined status in the index & worktree) 0342 */ 0343 static KDevelop::VcsStatusInfo::State extendedStateToBasic(const ExtendedState state); 0344 0345 QList<QStringList> branchesShas; 0346 QList<QUrl> m_urls; 0347 0348 /** Tells if it's older than 1.7.0 or not */ 0349 bool m_oldVersion = false; 0350 0351 KDirWatch* m_watcher; 0352 QList<QUrl> m_branchesChange; 0353 bool m_usePrefix = true; 0354 0355 /** A tree model tracking and classifying changes into staged, unstaged and untracked */ 0356 RepoStatusModel* m_repoStatusModel; 0357 0358 /** A factory for constructing the tool view for preparing commits */ 0359 CommitToolViewFactory* m_commitToolViewFactory; 0360 }; 0361 0362 Q_DECLARE_METATYPE(GitPlugin::StashItem) 0363 0364 QVariant runSynchronously(KDevelop::VcsJob* job); 0365 0366 #endif