File indexing completed on 2024-05-19 05:45:41
0001 /* 0002 SPDX-FileCopyrightText: 2021 Hamed Masafi <hamed.masfi@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 #include "statuscache.h" 0008 #include "qdebug.h" 0009 0010 #include <git2.h> 0011 0012 namespace Impl 0013 { 0014 struct wrapper { 0015 QMap<QString, KVersionControlPlugin::ItemVersion> files; 0016 QStringList ignores; 0017 QString currentPath; 0018 QString rootPath; 0019 QString prefix; 0020 bool allIgnored{false}; 0021 }; 0022 0023 KVersionControlPlugin::ItemVersion convertToItemVersion(unsigned int status_flags) 0024 { 0025 KVersionControlPlugin::ItemVersion status; 0026 if (status_flags & GIT_STATUS_WT_NEW) 0027 status = KVersionControlPlugin::ItemVersion::AddedVersion; 0028 else if (status_flags & GIT_STATUS_WT_MODIFIED) 0029 status = KVersionControlPlugin::ItemVersion::LocallyModifiedVersion; 0030 else if (status_flags & GIT_STATUS_WT_DELETED) 0031 status = KVersionControlPlugin::ItemVersion::RemovedVersion; 0032 else if (status_flags & GIT_STATUS_WT_RENAMED) 0033 status = KVersionControlPlugin::ItemVersion::ConflictingVersion; 0034 else if (status_flags & GIT_STATUS_IGNORED) 0035 status = KVersionControlPlugin::ItemVersion::IgnoredVersion; 0036 else 0037 status = KVersionControlPlugin::ItemVersion::UnversionedVersion; 0038 0039 return status; 0040 } 0041 0042 QString removeSlashAtEnd(const QString &s) 0043 { 0044 if (s.endsWith("/")) 0045 return s.mid(0, s.length() - 1); 0046 return s; 0047 } 0048 0049 auto callback(const char *pa, unsigned int status_flags, void *payload) -> int 0050 { 0051 auto w = reinterpret_cast<wrapper *>(payload); 0052 0053 auto path = QString{pa}; 0054 0055 if (w->prefix.startsWith(path) && status_flags & GIT_STATUS_IGNORED) { 0056 w->allIgnored = true; 0057 return 1; 0058 } 0059 if (!path.startsWith(w->currentPath)) { 0060 return 0; 0061 } 0062 0063 auto status = convertToItemVersion(status_flags); 0064 0065 QString entryName; 0066 QString childPath; 0067 if (path.indexOf("/", w->prefix.length()) == -1) { 0068 entryName = path.mid(w->prefix.length()); 0069 } else { 0070 entryName = path.mid(w->prefix.length(), path.indexOf("/", w->prefix.length()) + 1 - w->prefix.length()); 0071 childPath = path.mid(w->prefix.length() + entryName.length()); 0072 } 0073 0074 qDebug() << path << entryName << childPath; 0075 if (childPath.isEmpty()) { 0076 w->files.insert(removeSlashAtEnd(entryName), status); 0077 } else { 0078 if (status != KVersionControlPlugin::ItemVersion::IgnoredVersion) { 0079 // if (entryName.count('/') == 1 && entryName.endsWith("/")) 0080 // w->files.insert(childPath, KVersionControlPlugin::ItemVersion::IgnoredVersion); 0081 // } else { 0082 auto st = w->files.value(entryName, KVersionControlPlugin::ItemVersion::NormalVersion); 0083 0084 if (st != status) { 0085 if (st == KVersionControlPlugin::ItemVersion::NormalVersion) 0086 w->files.insert(removeSlashAtEnd(entryName), status); 0087 else 0088 w->files.insert(removeSlashAtEnd(entryName), KVersionControlPlugin::ItemVersion::LocallyModifiedVersion); 0089 } 0090 } 0091 } 0092 return 0; 0093 }; 0094 } 0095 0096 StatusCache::StatusCache() = default; 0097 0098 KVersionControlPlugin::ItemVersion StatusCache::status(const QString &name) 0099 { 0100 if (mCurrentPathIsIgnored || name.startsWith(".git/") || name == ".git") 0101 return KVersionControlPlugin::IgnoredVersion; 0102 0103 return mStatuses.value(name, KVersionControlPlugin::ItemVersion::NormalVersion); 0104 } 0105 0106 bool StatusCache::setPath(const QString &path) 0107 { 0108 if (mPath == path) 0109 return true; 0110 0111 mPath = path; 0112 0113 git_repository *repo{nullptr}; 0114 int n = git_repository_open_ext(&repo, path.toUtf8().data(), 0, NULL); 0115 0116 if (n) { 0117 git_repository_free(repo); 0118 return false; 0119 } 0120 0121 mRepoRootPath = git_repository_workdir(repo); 0122 if (path.startsWith(mRepoRootPath + ".git/")) { 0123 mCurrentPathIsIgnored = true; 0124 0125 git_repository_free(repo); 0126 return true; 0127 } 0128 0129 Impl::wrapper w; 0130 w.currentPath = path; 0131 if (!w.currentPath.endsWith("/")) 0132 w.currentPath.append("/"); 0133 w.rootPath = mRepoRootPath; 0134 if (!w.rootPath.endsWith("/")) 0135 w.rootPath.append("/"); 0136 w.prefix = w.currentPath.replace(w.rootPath, ""); 0137 0138 git_status_options opts; 0139 git_status_options_init(&opts, GIT_STATUS_OPTIONS_VERSION); 0140 opts.show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR; 0141 opts.flags = GIT_STATUS_OPT_DEFAULTS; 0142 0143 // if (!w.prefix.isEmpty()) { 0144 // char *paths[] = {w.prefix.mid(0, w.prefix.length() - 1).toLocal8Bit().data()}; 0145 // git_strarray array = {paths, 1}; 0146 // opts.pathspec = array; 0147 // } 0148 0149 git_status_foreach_ext(repo, &opts, Impl::callback, &w); 0150 mStatuses = w.files; 0151 mCurrentPathIsIgnored = w.allIgnored; 0152 0153 git_repository_free(repo); 0154 0155 return true; 0156 }