File indexing completed on 2024-04-28 05:49:10
0001 /* 0002 SPDX-FileCopyrightText: 2021 Waqar Ahmed <waqar.17a@gmail.com> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 #include "pushpulldialog.h" 0007 0008 #include <QFile> 0009 #include <QProcess> 0010 #include <QSettings> 0011 0012 #include <KConfigGroup> 0013 #include <KSharedConfig> 0014 #include <KTextEditor/MainWindow> 0015 #include <KTextEditor/View> 0016 0017 #include <gitprocess.h> 0018 #include <hostprocess.h> 0019 #include <ktexteditor_utils.h> 0020 0021 PushPullDialog::PushPullDialog(KTextEditor::MainWindow *mainWindow, const QString &repoPath) 0022 : HUDDialog(nullptr, mainWindow->window()) 0023 , m_repo(repoPath) 0024 { 0025 m_lineEdit.setFont(Utils::editorFont()); 0026 m_treeView.setFont(Utils::editorFont()); 0027 setFilteringEnabled(false); 0028 loadLastExecutedCommands(); 0029 detectGerrit(); 0030 } 0031 0032 void PushPullDialog::openDialog(PushPullDialog::Mode m) 0033 { 0034 // build the string 0035 QStringList builtStrings; 0036 if (m == Push && m_isGerrit) { 0037 builtStrings << QStringLiteral("git push origin HEAD:refs/for/%1").arg(m_gerritBranch); 0038 } else { 0039 builtStrings = buildCmdStrings(m); 0040 } 0041 // find if we have a last executed push/pull command 0042 QString lastCmd = getLastPushPullCmd(m); 0043 0044 QStringList lastExecCmds = m_lastExecutedCommands; 0045 0046 // if found, bring it up 0047 if (!lastCmd.isEmpty()) { 0048 lastExecCmds.removeAll(lastCmd); 0049 lastExecCmds.push_front(lastCmd); 0050 } 0051 0052 for (const auto &s : builtStrings) { 0053 lastExecCmds.removeAll(s); 0054 lastExecCmds.push_front(s); 0055 } 0056 0057 setStringList(lastExecCmds); 0058 0059 connect(m_treeView.selectionModel(), &QItemSelectionModel::currentChanged, this, [this](const QModelIndex ¤t, const QModelIndex &) { 0060 m_lineEdit.setText(current.data().toString()); 0061 }); 0062 0063 reselectFirst(); 0064 0065 exec(); 0066 } 0067 0068 QString PushPullDialog::getLastPushPullCmd(Mode m) const 0069 { 0070 const QString cmdToFind = m == Push ? QStringLiteral("git push") : QStringLiteral("git pull"); 0071 QString found; 0072 for (const auto &cmd : m_lastExecutedCommands) { 0073 if (cmd.startsWith(cmdToFind)) { 0074 found = cmd; 0075 break; 0076 } 0077 } 0078 return found; 0079 } 0080 0081 void PushPullDialog::loadLastExecutedCommands() 0082 { 0083 KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("kategit")); 0084 m_lastExecutedCommands = config.readEntry("lastExecutedGitCmds", QStringList()); 0085 } 0086 0087 void PushPullDialog::saveCommand(const QString &command) 0088 { 0089 KConfigGroup config(KSharedConfig::openConfig(), QStringLiteral("kategit")); 0090 QStringList cmds = m_lastExecutedCommands; 0091 cmds.removeAll(command); 0092 cmds.push_front(command); 0093 while (cmds.size() > 8) { 0094 cmds.pop_back(); 0095 } 0096 config.writeEntry("lastExecutedGitCmds", cmds); 0097 } 0098 0099 /** 0100 * This is not for display, hence not reusing gitutils here 0101 */ 0102 static QString currentBranchName(const QString &repo) 0103 { 0104 QProcess git; 0105 if (!setupGitProcess(git, repo, {QStringLiteral("symbolic-ref"), QStringLiteral("--short"), QStringLiteral("HEAD")})) { 0106 return {}; 0107 } 0108 0109 startHostProcess(git, QIODevice::ReadOnly); 0110 if (git.waitForStarted() && git.waitForFinished(-1)) { 0111 if (git.exitStatus() == QProcess::NormalExit && git.exitCode() == 0) { 0112 return QString::fromUtf8(git.readAllStandardOutput().trimmed()); 0113 } 0114 } 0115 // give up 0116 return {}; 0117 } 0118 0119 static QStringList remotesList(const QString &repo) 0120 { 0121 QProcess git; 0122 if (!setupGitProcess(git, repo, {QStringLiteral("remote")})) { 0123 return {}; 0124 } 0125 0126 startHostProcess(git, QIODevice::ReadOnly); 0127 if (git.waitForStarted() && git.waitForFinished(-1)) { 0128 if (git.exitStatus() == QProcess::NormalExit && git.exitCode() == 0) { 0129 return QString::fromUtf8(git.readAllStandardOutput()).split(QLatin1Char('\n'), Qt::SkipEmptyParts); 0130 } 0131 } 0132 return {}; 0133 } 0134 0135 static QString getRemoteForCurrentBranch(const QString &repo, const QString &branch) 0136 { 0137 QProcess git; 0138 const QStringList args{QStringLiteral("config"), QStringLiteral("branch.%1.remote").arg(branch)}; 0139 if (!setupGitProcess(git, repo, args)) { 0140 return {}; 0141 } 0142 0143 startHostProcess(git, QIODevice::ReadOnly); 0144 if (git.waitForStarted() && git.waitForFinished(-1)) { 0145 if (git.exitStatus() == QProcess::NormalExit && git.exitCode() == 0) { 0146 return QString::fromUtf8(git.readAllStandardOutput().trimmed()); 0147 } 0148 } 0149 return {}; 0150 } 0151 0152 QStringList PushPullDialog::buildCmdStrings(Mode m) 0153 { 0154 const QString arg = m == Push ? QLatin1String("push") : QLatin1String("pull"); 0155 const auto br = currentBranchName(m_repo); 0156 if (br.isEmpty()) { 0157 return {QStringLiteral("git %1").arg(arg)}; 0158 } 0159 0160 auto remoteForBranch = getRemoteForCurrentBranch(m_repo, br); 0161 if (remoteForBranch.isEmpty()) { 0162 const auto remotes = remotesList(m_repo); 0163 if (remotes.isEmpty()) { 0164 return {QStringLiteral("git %1").arg(arg)}; 0165 } 0166 QStringList cmds; 0167 // reverse traversal as later, these commands will be pushed in front of the 0168 // list displayed to user, so we invert the order here and it will appear in 0169 // the same order that git shows 0170 for (auto ri = remotes.crbegin(); ri != remotes.crend(); ++ri) { 0171 cmds << QStringLiteral("git %1 %2 %3").arg(arg, *ri, br); 0172 } 0173 return cmds; 0174 } else { 0175 // if we found a remote, only offer that 0176 return {QStringLiteral("git %1 %2 %3").arg(arg, remoteForBranch, br)}; 0177 } 0178 } 0179 0180 void PushPullDialog::slotReturnPressed(const QModelIndex &) 0181 { 0182 if (!m_lineEdit.text().isEmpty()) { 0183 auto args = m_lineEdit.text().split(QLatin1Char(' ')); 0184 if (args.first() == QStringLiteral("git")) { 0185 saveCommand(m_lineEdit.text()); 0186 args.pop_front(); 0187 Q_EMIT runGitCommand(args); 0188 } 0189 } 0190 0191 hide(); 0192 } 0193 0194 void PushPullDialog::detectGerrit() 0195 { 0196 if (QFile::exists(m_repo + QLatin1String(".gitreview"))) { 0197 m_isGerrit = true; 0198 QSettings s(m_repo + QLatin1String("/.gitreview"), QSettings::IniFormat); 0199 m_gerritBranch = s.value(QStringLiteral("gerrit/defaultbranch")).toString(); 0200 } 0201 } 0202 0203 #include "moc_pushpulldialog.cpp"