File indexing completed on 2024-04-28 05:49:27
0001 /* 0002 SPDX-FileCopyrightText: 2010 Christoph Cullmann <cullmann@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "gitprocess.h" 0008 #include "hostprocess.h" 0009 0010 #include <QProcess> 0011 #include <QRegularExpression> 0012 #include <QStandardPaths> 0013 0014 #include <QIcon> 0015 0016 #include <KIconLoader> 0017 0018 bool setupGitProcess(QProcess &process, const QString &workingDirectory, const QStringList &arguments) 0019 { 0020 // only use git from PATH 0021 static const auto gitExecutable = safeExecutableName(QStringLiteral("git")); 0022 if (gitExecutable.isEmpty()) { 0023 // ensure we have no valid QProcess setup 0024 process.setProgram(QString()); 0025 return false; 0026 } 0027 0028 // setup program and arguments, ensure we do run git in the right working directory 0029 process.setProgram(gitExecutable); 0030 process.setWorkingDirectory(workingDirectory); 0031 process.setArguments(arguments); 0032 0033 /** 0034 * from the git manual: 0035 * 0036 * If set to 0, Git will complete any requested operation without performing any optional sub-operations that require taking a lock. 0037 * For example, this will prevent git status from refreshing the index as a side effect. 0038 * This is useful for processes running in the background which do not want to cause lock contention with other operations on the repository. 0039 * Defaults to 1. 0040 * 0041 * we use the env var as this is compatible even for "ancient" git versions pre 2.15.2 0042 */ 0043 QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); 0044 env.insert(QStringLiteral("GIT_OPTIONAL_LOCKS"), QStringLiteral("0")); 0045 process.setProcessEnvironment(env); 0046 return true; 0047 } 0048 0049 // internal helper for the external caching accessor 0050 static std::pair<int, int> getGitVersionUncached(const QString &workingDir) 0051 { 0052 QProcess git; 0053 if (!setupGitProcess(git, workingDir, {QStringLiteral("--version")})) { 0054 return {-1, -1}; 0055 } 0056 0057 // try to run, no version output feasible if not possible 0058 startHostProcess(git, QProcess::ReadOnly); 0059 if (!git.waitForStarted() || !git.waitForFinished() || git.exitStatus() != QProcess::NormalExit || git.exitCode() != 0) { 0060 return {-1, -1}; 0061 } 0062 0063 // match the version output 0064 const QString gitVersion = QString::fromUtf8(git.readAllStandardOutput()); 0065 const QRegularExpression gitRegex(QStringLiteral("git version\\s*(\\d+).(\\d+).(\\d+)+.*")); 0066 const QRegularExpressionMatch gitMatch = gitRegex.match(gitVersion); 0067 0068 bool okMajor = false; 0069 bool okMinor = false; 0070 const int versionMajor = gitMatch.captured(1).toInt(&okMajor); 0071 const int versionMinor = gitMatch.captured(2).toInt(&okMinor); 0072 if (okMajor && okMinor) { 0073 return {versionMajor, versionMinor}; 0074 } 0075 0076 // no version properly detected 0077 return {-1, -1}; 0078 } 0079 0080 std::pair<int, int> getGitVersion(const QString &workingDir) 0081 { 0082 // cache internal result to avoid expensive recalculation 0083 static const auto cachedVersion = getGitVersionUncached(workingDir); 0084 return cachedVersion; 0085 } 0086 0087 std::optional<QString> getRepoBasePath(const QString &repo) 0088 { 0089 /* This call is intentionally blocking because we need git path for everything else */ 0090 QProcess git; 0091 if (!setupGitProcess(git, repo, {QStringLiteral("rev-parse"), QStringLiteral("--show-toplevel")})) { 0092 return std::nullopt; 0093 } 0094 0095 startHostProcess(git, QProcess::ReadOnly); 0096 if (git.waitForStarted() && git.waitForFinished(-1)) { 0097 if (git.exitStatus() != QProcess::NormalExit || git.exitCode() != 0) { 0098 return std::nullopt; 0099 } 0100 QString dotGitPath = QString::fromUtf8(git.readAllStandardOutput().trimmed()); 0101 if (!dotGitPath.endsWith(QLatin1Char('/'))) { 0102 dotGitPath.append(QLatin1Char('/')); 0103 } 0104 return dotGitPath; 0105 } 0106 return std::nullopt; 0107 } 0108 0109 QIcon gitIcon() 0110 { 0111 static const auto icon = KDE::icon(QStringLiteral(":/icons/icons/sc-apps-git.svg")); 0112 return icon; 0113 }