File indexing completed on 2024-05-05 10:02:25
0001 /* 0002 SPDX-FileCopyrightText: 2011 Vishesh Yadav <vishesh3y@gmail.com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "hgwrapper.h" 0008 0009 #include <QApplication> 0010 #include <QTextCodec> 0011 #include <QUrl> 0012 #include <QDebug> 0013 #include <QRegularExpression> 0014 0015 //TODO: Replace start() with executeCommand functions wherever possible. 0016 //FIXME: Add/Remove/Revert argument length limit. Divide the list. 0017 //FIXME: Cannot create thread for parent that is in different thread. 0018 0019 HgWrapper *HgWrapper::m_instance = nullptr; 0020 0021 HgWrapper::HgWrapper(QObject *parent) : 0022 QObject(parent) 0023 { 0024 m_localCodec = QTextCodec::codecForLocale(); 0025 0026 // re-emit QProcess signals 0027 connect(&m_process, &QProcess::errorOccurred, 0028 this, &HgWrapper::errorOccurred); 0029 connect(&m_process, &QProcess::finished, 0030 this, &HgWrapper::finished); 0031 connect(&m_process, &QProcess::stateChanged, 0032 this, &HgWrapper::stateChanged); 0033 connect(&m_process, &QProcess::started, 0034 this, &HgWrapper::started); 0035 0036 connect(&m_process, &QProcess::finished, 0037 this, &HgWrapper::slotOperationCompleted); 0038 connect(&m_process, &QProcess::errorOccurred, 0039 this, &HgWrapper::slotOperationError); 0040 0041 } 0042 0043 HgWrapper *HgWrapper::instance() 0044 { 0045 if (!m_instance) { 0046 m_instance = new HgWrapper; 0047 } 0048 return m_instance; 0049 } 0050 0051 void HgWrapper::freeInstance() 0052 { 0053 delete m_instance; 0054 m_instance = nullptr; 0055 } 0056 0057 void HgWrapper::slotOperationCompleted(int exitCode, 0058 QProcess::ExitStatus exitStatus) 0059 { 0060 qDebug() << "'hg' Exit Code: " << exitCode << " Exit Status: " 0061 << exitStatus; 0062 if (m_primaryOperation) { 0063 Q_EMIT primaryOperationFinished(exitCode, exitStatus); 0064 } 0065 } 0066 0067 void HgWrapper::slotOperationError(QProcess::ProcessError error) 0068 { 0069 qDebug() << "Error occurred while executing 'hg' with arguments "; 0070 if (m_primaryOperation) { 0071 Q_EMIT primaryOperationError(error); 0072 } 0073 } 0074 0075 bool HgWrapper::executeCommand(const QString &hgCommand, 0076 const QStringList &arguments, 0077 QString &output, 0078 bool primaryOperation) 0079 { 0080 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0081 0082 executeCommand(hgCommand, arguments, primaryOperation); 0083 m_process.waitForFinished(); 0084 output = QTextCodec::codecForLocale()->toUnicode(m_process.readAllStandardOutput()); 0085 0086 return (m_process.exitStatus() == QProcess::NormalExit && 0087 m_process.exitCode() == 0); 0088 } 0089 0090 void HgWrapper::executeCommand(const QString &hgCommand, 0091 const QStringList &arguments, 0092 bool primaryOperation) 0093 { 0094 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0095 0096 m_primaryOperation = primaryOperation; 0097 if (m_primaryOperation) { 0098 qDebug() << "Primary operation"; 0099 } 0100 0101 QStringList args; 0102 args << hgCommand; 0103 args << arguments; 0104 m_process.setWorkingDirectory(m_currentDir); 0105 m_process.start(QLatin1String("hg"), args); 0106 } 0107 0108 bool HgWrapper::executeCommandTillFinished(const QString &hgCommand, 0109 const QStringList &arguments, 0110 bool primaryOperation) 0111 { 0112 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0113 0114 m_primaryOperation = primaryOperation; 0115 0116 QStringList args; 0117 args << hgCommand; 0118 args << arguments; 0119 m_process.setWorkingDirectory(m_currentDir); 0120 m_process.start(QLatin1String("hg"), args); 0121 m_process.waitForFinished(); 0122 0123 return (m_process.exitStatus() == QProcess::NormalExit && 0124 m_process.exitCode() == 0); 0125 } 0126 0127 QString HgWrapper::getBaseDir() const 0128 { 0129 return m_hgBaseDir; 0130 } 0131 0132 QString HgWrapper::getCurrentDir() const 0133 { 0134 return m_currentDir; 0135 } 0136 0137 void HgWrapper::updateBaseDir() 0138 { 0139 m_process.setWorkingDirectory(m_currentDir); 0140 m_process.start(QStringLiteral("hg"), QStringList{QStringLiteral("root")}); 0141 m_process.waitForFinished(); 0142 m_hgBaseDir = QString::fromLocal8Bit(m_process.readAllStandardOutput()).trimmed(); 0143 } 0144 0145 void HgWrapper::setCurrentDir(const QString &directory) 0146 { 0147 m_currentDir = directory; 0148 updateBaseDir(); //now get root directory of repository 0149 } 0150 0151 void HgWrapper::setBaseAsWorkingDir() 0152 { 0153 m_process.setWorkingDirectory(getBaseDir()); 0154 } 0155 0156 void HgWrapper::addFiles(const KFileItemList &fileList) 0157 { 0158 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0159 0160 QStringList args; 0161 args << QStringLiteral("add"); 0162 for (const KFileItem &item : fileList) { 0163 args << item.localPath(); 0164 } 0165 m_process.start(QStringLiteral("hg"), args); 0166 } 0167 0168 bool HgWrapper::renameFile(const QString &source, const QString &destination) 0169 { 0170 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0171 0172 QStringList args; 0173 args << source << destination; 0174 executeCommand(QStringLiteral("rename"), args, true); 0175 0176 m_process.waitForFinished(); 0177 return (m_process.exitStatus() == QProcess::NormalExit && 0178 m_process.exitCode() == 0); 0179 } 0180 0181 void HgWrapper::removeFiles(const KFileItemList &fileList) 0182 { 0183 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0184 0185 QStringList args{ 0186 QStringLiteral("remove"), 0187 QStringLiteral("--force"), 0188 }; 0189 for (const KFileItem &item : fileList) { 0190 args << item.localPath(); 0191 } 0192 m_process.start(QStringLiteral("hg"), args); 0193 } 0194 0195 bool HgWrapper::commit(const QString &message, const QStringList &files, 0196 bool closeCurrentBranch) 0197 { 0198 QStringList args; 0199 args << files; 0200 args << QStringLiteral("-m") << message; 0201 if (closeCurrentBranch) { 0202 args << QStringLiteral("--close-branch"); 0203 } 0204 executeCommand(QStringLiteral("commit"), args, true); 0205 m_process.waitForFinished(); 0206 return (m_process.exitCode() == 0 && 0207 m_process.exitStatus() == QProcess::NormalExit); 0208 } 0209 0210 bool HgWrapper::createBranch(const QString &name) 0211 { 0212 QStringList args; 0213 args << name; 0214 executeCommand(QStringLiteral("branch"), args, true); 0215 m_process.waitForFinished(); 0216 return (m_process.exitCode() == 0 && 0217 m_process.exitStatus() == QProcess::NormalExit); 0218 } 0219 0220 bool HgWrapper::switchBranch(const QString &name) 0221 { 0222 const QStringList args{ 0223 QStringLiteral("-c"), 0224 name, 0225 }; 0226 executeCommand(QStringLiteral("update"), args, true); 0227 m_process.waitForFinished(); 0228 return (m_process.exitCode() == 0 && 0229 m_process.exitStatus() == QProcess::NormalExit); 0230 } 0231 0232 bool HgWrapper::createTag(const QString &name) 0233 { 0234 QStringList args; 0235 args << name; 0236 executeCommand(QStringLiteral("tag"), args, true); 0237 m_process.waitForFinished(); 0238 return (m_process.exitCode() == 0 && 0239 m_process.exitStatus() == QProcess::NormalExit); 0240 } 0241 0242 bool HgWrapper::revertAll() 0243 { 0244 QStringList args; 0245 args << QStringLiteral("--all"); 0246 return executeCommandTillFinished(QStringLiteral("revert"), args, true); 0247 } 0248 0249 0250 bool HgWrapper::revert(const KFileItemList &fileList) 0251 { 0252 QStringList arguments; 0253 for (const KFileItem &item : fileList) { 0254 arguments << item.localPath(); 0255 } 0256 return executeCommandTillFinished(QStringLiteral("revert"), arguments, true); 0257 } 0258 0259 bool HgWrapper::rollback(bool dryRun) 0260 { 0261 QStringList args; 0262 if (dryRun) { 0263 args << QStringLiteral("-n"); 0264 } 0265 return executeCommandTillFinished(QStringLiteral("rollback"), args, true); 0266 } 0267 0268 bool HgWrapper::switchTag(const QString &name) 0269 { 0270 QStringList args; 0271 args << QStringLiteral("-c") << name; 0272 executeCommand(QStringLiteral("update"), args, true); 0273 m_process.waitForFinished(); 0274 return (m_process.exitCode() == 0 && 0275 m_process.exitStatus() == QProcess::NormalExit); 0276 } 0277 0278 //TODO: Make it return QStringList. 0279 QString HgWrapper::getParentsOfHead() 0280 { 0281 Q_ASSERT(m_process.state() == QProcess::NotRunning); 0282 0283 QString output; 0284 const QStringList args{ 0285 QStringLiteral("--template"), 0286 QStringLiteral("{rev}:{node|short} "), 0287 }; 0288 executeCommand(QStringLiteral("parents"), args, output); 0289 return output; 0290 } 0291 0292 QStringList HgWrapper::getTags() 0293 { 0294 QStringList result; 0295 executeCommand(QStringLiteral("tags")); 0296 while (m_process.waitForReadyRead()) { 0297 char buffer[1048]; 0298 while (m_process.readLine(buffer, sizeof(buffer)) > 0) { 0299 result << QString::fromLocal8Bit(buffer).split(QRegularExpression(QStringLiteral("\\s+")), 0300 Qt::SkipEmptyParts).first(); 0301 } 0302 } 0303 return result; 0304 } 0305 0306 QStringList HgWrapper::getBranches() 0307 { 0308 QStringList result; 0309 executeCommand(QStringLiteral("branches")); 0310 while (m_process.waitForReadyRead()) { 0311 char buffer[1048]; 0312 while (m_process.readLine(buffer, sizeof(buffer)) > 0) { 0313 // 'hg branches' command lists the branches in following format 0314 // <branchname> <revision:changeset_hash> [(inactive)] 0315 // Extract just the branchname 0316 result << QString::fromLocal8Bit(buffer).remove(QRegularExpression(QStringLiteral("[\\s]+[\\d:a-zA-Z\\(\\)]*"))); 0317 } 0318 } 0319 return result; 0320 } 0321 0322 void HgWrapper::getItemVersions(QHash<QString, KVersionControlPlugin::ItemVersion> &result) 0323 { 0324 /*int nTrimOutLeft = m_hgBaseDir.length(); 0325 QString relativePrefix = m_currentDir.right(m_currentDir.length() - 0326 nTrimOutLeft - 1); 0327 qDebug() << m_hgBaseDir << " " << relativePrefix;*/ 0328 0329 // Get status of files 0330 const QStringList args{ 0331 QStringLiteral("status"), 0332 QStringLiteral("--modified"), 0333 QStringLiteral("--added"), 0334 QStringLiteral("--removed"), 0335 QStringLiteral("--deleted"), 0336 QStringLiteral("--unknown"), 0337 QStringLiteral("--ignored"), 0338 }; 0339 m_process.setWorkingDirectory(m_currentDir); 0340 m_process.start(QStringLiteral("hg"), args); 0341 while (m_process.waitForReadyRead()) { 0342 char buffer[1024]; 0343 while (m_process.readLine(buffer, sizeof(buffer)) > 0) { 0344 const QString currentLine(QTextCodec::codecForLocale()->toUnicode(buffer).trimmed()); 0345 char currentStatus = buffer[0]; 0346 QString currentFile = currentLine.mid(2); 0347 KVersionControlPlugin::ItemVersion vs = KVersionControlPlugin::NormalVersion; 0348 switch (currentStatus) { 0349 case 'A': 0350 vs = KVersionControlPlugin::AddedVersion; 0351 break; 0352 case 'M': 0353 vs = KVersionControlPlugin::LocallyModifiedVersion; 0354 break; 0355 case '?': 0356 vs = KVersionControlPlugin::UnversionedVersion; 0357 break; 0358 case 'R': 0359 vs = KVersionControlPlugin::RemovedVersion; 0360 break; 0361 case 'I': 0362 vs = KVersionControlPlugin::IgnoredVersion; 0363 break; 0364 case 'C': 0365 vs = KVersionControlPlugin::NormalVersion; 0366 break; 0367 case '!': 0368 vs = KVersionControlPlugin::MissingVersion; 0369 break; 0370 } 0371 if (vs != KVersionControlPlugin::NormalVersion) { 0372 // Get full path to file and insert it to result 0373 QUrl url = QUrl::fromLocalFile(m_hgBaseDir); 0374 url = url.adjusted(QUrl::StripTrailingSlash); 0375 url.setPath(url.path() + QLatin1Char('/') + currentFile); 0376 QString filePath = url.path(); 0377 result.insert(filePath, vs); 0378 } 0379 } 0380 } 0381 } 0382 0383 void HgWrapper::terminateCurrentProcess() 0384 { 0385 qDebug() << "terminating"; 0386 m_process.terminate(); 0387 } 0388 0389 bool HgWrapper::isWorkingDirectoryClean() 0390 { 0391 const QStringList args { 0392 QStringLiteral("--modified"), 0393 QStringLiteral("--added"), 0394 QStringLiteral("--removed"), 0395 QStringLiteral("--deleted"), 0396 }; 0397 0398 QString output; 0399 executeCommand(QStringLiteral("status"), args, output); 0400 0401 return output.trimmed().isEmpty(); 0402 } 0403 0404 0405 0406 #include "moc_hgwrapper.cpp"