File indexing completed on 2024-05-12 05:51:44
0001 /* 0002 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "gitutils.h" 0008 0009 #include "hostprocess.h" 0010 #include <gitprocess.h> 0011 0012 #include <QProcess> 0013 0014 bool GitUtils::isGitRepo(const QString &repo) 0015 { 0016 QProcess git; 0017 if (!setupGitProcess(git, repo, {QStringLiteral("rev-parse"), QStringLiteral("--is-inside-work-tree")})) { 0018 return false; 0019 } 0020 0021 startHostProcess(git, QProcess::ReadOnly); 0022 if (git.waitForStarted() && git.waitForFinished(-1)) { 0023 return git.readAll().trimmed() == "true"; 0024 } 0025 return false; 0026 } 0027 0028 QString GitUtils::getCurrentBranchName(const QString &repo) 0029 { 0030 // clang-format off 0031 QStringList argsList[3] = 0032 { 0033 {QStringLiteral("symbolic-ref"), QStringLiteral("--short"), QStringLiteral("HEAD")}, 0034 {QStringLiteral("describe"), QStringLiteral("--exact-match"), QStringLiteral("HEAD")}, 0035 {QStringLiteral("rev-parse"), QStringLiteral("--short"), QStringLiteral("HEAD")} 0036 }; 0037 // clang-format on 0038 0039 for (int i = 0; i < 3; ++i) { 0040 QProcess git; 0041 if (!setupGitProcess(git, repo, argsList[i])) { 0042 return QString(); 0043 } 0044 0045 startHostProcess(git, QProcess::ReadOnly); 0046 if (git.waitForStarted() && git.waitForFinished(-1)) { 0047 if (git.exitStatus() == QProcess::NormalExit && git.exitCode() == 0) { 0048 return QString::fromUtf8(git.readAllStandardOutput().trimmed()); 0049 } 0050 } 0051 } 0052 0053 // give up 0054 return QString(); 0055 } 0056 0057 GitUtils::CheckoutResult GitUtils::checkoutBranch(const QString &repo, const QString &branch) 0058 { 0059 QProcess git; 0060 if (!setupGitProcess(git, repo, {QStringLiteral("checkout"), branch})) { 0061 return CheckoutResult{}; 0062 } 0063 0064 startHostProcess(git, QProcess::ReadOnly); 0065 CheckoutResult res; 0066 res.branch = branch; 0067 if (git.waitForStarted() && git.waitForFinished(-1)) { 0068 res.returnCode = git.exitCode(); 0069 res.error = QString::fromUtf8(git.readAllStandardError()); 0070 } 0071 return res; 0072 } 0073 0074 GitUtils::CheckoutResult GitUtils::checkoutNewBranch(const QString &repo, const QString &newBranch, const QString &fromBranch) 0075 { 0076 QProcess git; 0077 QStringList args{QStringLiteral("checkout"), QStringLiteral("-q"), QStringLiteral("-b"), newBranch}; 0078 if (!fromBranch.isEmpty()) { 0079 args.append(fromBranch); 0080 } 0081 0082 if (!setupGitProcess(git, repo, args)) { 0083 return CheckoutResult{}; 0084 } 0085 0086 startHostProcess(git, QProcess::ReadOnly); 0087 CheckoutResult res; 0088 res.branch = newBranch; 0089 if (git.waitForStarted() && git.waitForFinished(-1)) { 0090 res.returnCode = git.exitCode(); 0091 res.error = QString::fromUtf8(git.readAllStandardError()); 0092 } 0093 return res; 0094 } 0095 0096 static GitUtils::Branch parseLocalBranch(const QString &raw) 0097 { 0098 static const int len = QStringLiteral("refs/heads/").length(); 0099 return GitUtils::Branch{raw.mid(len), QString(), GitUtils::Head, QString()}; 0100 } 0101 0102 static GitUtils::Branch parseRemoteBranch(const QString &raw) 0103 { 0104 static const int len = QStringLiteral("refs/remotes/").length(); 0105 int indexofRemote = raw.indexOf(QLatin1Char('/'), len); 0106 return GitUtils::Branch{raw.mid(len), raw.mid(len, indexofRemote - len), GitUtils::Remote, QString()}; 0107 } 0108 0109 QList<GitUtils::Branch> GitUtils::getAllBranchesAndTags(const QString &repo, RefType ref) 0110 { 0111 // git for-each-ref --format '%(refname)' --sort=-committerdate ... 0112 QProcess git; 0113 0114 QStringList args{QStringLiteral("for-each-ref"), QStringLiteral("--format"), QStringLiteral("%(refname)"), QStringLiteral("--sort=-committerdate")}; 0115 if (ref & RefType::Head) { 0116 args.append(QStringLiteral("refs/heads")); 0117 } 0118 if (ref & RefType::Remote) { 0119 args.append(QStringLiteral("refs/remotes")); 0120 } 0121 if (ref & RefType::Tag) { 0122 args.append(QStringLiteral("refs/tags")); 0123 args.append(QStringLiteral("--sort=-taggerdate")); 0124 } 0125 0126 if (!setupGitProcess(git, repo, args)) { 0127 return {}; 0128 } 0129 0130 startHostProcess(git, QProcess::ReadOnly); 0131 QList<Branch> branches; 0132 if (git.waitForStarted() && git.waitForFinished(-1)) { 0133 QString gitout = QString::fromUtf8(git.readAllStandardOutput()); 0134 QStringList out = gitout.split(QLatin1Char('\n')); 0135 0136 branches.reserve(out.size()); 0137 // clang-format off 0138 for (const auto &o : out) { 0139 if (ref & Head && o.startsWith(QLatin1String("refs/heads"))) { 0140 branches.append(parseLocalBranch(o)); 0141 } else if (ref & Remote && o.startsWith(QLatin1String("refs/remotes"))) { 0142 branches.append(parseRemoteBranch(o)); 0143 } else if (ref & Tag && o.startsWith(QLatin1String("refs/tags/"))) { 0144 static const int len = QStringLiteral("refs/tags/").length(); 0145 branches.append({o.mid(len), {}, RefType::Tag, QString()}); 0146 } 0147 } 0148 // clang-format on 0149 } 0150 0151 return branches; 0152 } 0153 0154 QList<GitUtils::Branch> GitUtils::getAllLocalBranchesWithLastCommitSubject(const QString &repo) 0155 { 0156 // git for-each-ref --format '%(refname)' --sort=-committerdate ... 0157 QProcess git; 0158 0159 QStringList args{QStringLiteral("for-each-ref"), 0160 QStringLiteral("--format"), 0161 QStringLiteral("%(refname)[--]%(contents:subject)"), 0162 QStringLiteral("--sort=-committerdate"), 0163 QStringLiteral("refs/heads")}; 0164 0165 if (!setupGitProcess(git, repo, args)) { 0166 return {}; 0167 } 0168 0169 startHostProcess(git, QProcess::ReadOnly); 0170 QList<Branch> branches; 0171 if (git.waitForStarted() && git.waitForFinished(-1)) { 0172 QByteArray gitout = git.readAllStandardOutput(); 0173 QByteArrayList rows = gitout.split('\n'); 0174 0175 branches.reserve(rows.size()); 0176 constexpr int len = sizeof("refs/heads/") - 1; 0177 for (const auto &row : rows) { 0178 int seperatorIdx = row.indexOf("[--]", len); 0179 if (seperatorIdx == -1) { 0180 continue; 0181 } 0182 int commitStart = seperatorIdx + 4; 0183 branches << GitUtils::Branch{QString::fromUtf8(row.mid(len, seperatorIdx - len)), 0184 QString(), 0185 GitUtils::Head, 0186 QString::fromUtf8(row.mid(commitStart))}; 0187 } 0188 } 0189 0190 return branches; 0191 } 0192 0193 QList<GitUtils::Branch> GitUtils::getAllBranches(const QString &repo) 0194 { 0195 return getAllBranchesAndTags(repo, static_cast<RefType>(RefType::Head | RefType::Remote)); 0196 } 0197 0198 std::pair<QString, QString> GitUtils::getLastCommitMessage(const QString &repo) 0199 { 0200 // git log -1 --pretty=%B 0201 QProcess git; 0202 if (!setupGitProcess(git, repo, {QStringLiteral("log"), QStringLiteral("-1"), QStringLiteral("--pretty=%B")})) { 0203 return {}; 0204 } 0205 0206 startHostProcess(git, QProcess::ReadOnly); 0207 if (git.waitForStarted() && git.waitForFinished(-1)) { 0208 if (git.exitCode() != 0 || git.exitStatus() != QProcess::NormalExit) { 0209 return {}; 0210 } 0211 0212 QList<QByteArray> output = git.readAllStandardOutput().split('\n'); 0213 if (output.isEmpty()) { 0214 return {}; 0215 } 0216 0217 QString msg = QString::fromUtf8(output.at(0)); 0218 QString desc; 0219 if (output.size() > 1) { 0220 desc = std::accumulate(output.cbegin() + 1, output.cend(), QString::fromUtf8(output.at(1)), [](const QString &line, const QByteArray &ba) { 0221 return QString(line + QString::fromUtf8(ba) + QStringLiteral("\n")); 0222 }); 0223 desc = desc.trimmed(); 0224 } 0225 return {msg, desc}; 0226 } 0227 return {}; 0228 } 0229 0230 GitUtils::Result GitUtils::deleteBranches(const QStringList &branches, const QString &repo) 0231 { 0232 QStringList args = {QStringLiteral("branch"), QStringLiteral("-D")}; 0233 args << branches; 0234 0235 QProcess git; 0236 if (!setupGitProcess(git, repo, args)) { 0237 return {}; 0238 } 0239 0240 startHostProcess(git, QProcess::ReadOnly); 0241 if (git.waitForStarted() && git.waitForFinished(-1)) { 0242 QString out = QString::fromLatin1(git.readAllStandardError()) + QString::fromLatin1(git.readAllStandardOutput()); 0243 return {out, git.exitCode()}; 0244 } 0245 Q_UNREACHABLE(); 0246 return {QString(), -1}; 0247 }