File indexing completed on 2024-05-05 04:39:01

0001 /*
0002     SPDX-FileCopyrightText: 2013-2014 Maciej Poleski
0003 
0004     SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0005 */
0006 
0007 #include "bazaarutils.h"
0008 
0009 #include <QDateTime>
0010 #include <QDebug>
0011 
0012 #include <vcs/vcsrevision.h>
0013 #include <vcs/vcsstatusinfo.h>
0014 #include <vcs/vcsevent.h>
0015 
0016 QDir BazaarUtils::toQDir(const QUrl& url)
0017 {
0018     return QDir(url.toLocalFile());
0019 }
0020 
0021 QDir BazaarUtils::workingCopy(const QUrl& path)
0022 {
0023     QDir dir = BazaarUtils::toQDir(path);
0024     while (!dir.exists(QStringLiteral(".bzr")) && dir.cdUp());
0025 
0026     return dir;
0027 }
0028 
0029 QString BazaarUtils::getRevisionSpec(const KDevelop::VcsRevision& revision)
0030 {
0031     if (revision.revisionType() == KDevelop::VcsRevision::Special) {
0032         if (revision.specialType() == KDevelop::VcsRevision::Head)
0033             return QStringLiteral("-rlast:1");
0034         else if (revision.specialType() == KDevelop::VcsRevision::Base)
0035             return QString();  // Workaround strange KDevelop behaviour
0036         else if (revision.specialType() == KDevelop::VcsRevision::Working)
0037             return QString();
0038         else if (revision.specialType() == KDevelop::VcsRevision::Start)
0039             return QStringLiteral("-r1");
0040         else
0041             return QString(); // Don't know how to handle this situation
0042     } else if (revision.revisionType() == KDevelop::VcsRevision::GlobalNumber)
0043         return QLatin1String("-r") + QString::number(revision.revisionValue().toLongLong());
0044     else
0045         return QString(); // Don't know how to handle this situation
0046 }
0047 
0048 QString BazaarUtils::getRevisionSpecRange(const KDevelop::VcsRevision& end)
0049 {
0050     if (end.revisionType() == KDevelop::VcsRevision::Special) {
0051         if (end.specialType() == KDevelop::VcsRevision::Head) {
0052             return QStringLiteral("-r..last:1");
0053         } else if (end.specialType() == KDevelop::VcsRevision::Base) {
0054             return QStringLiteral("-r..last:1"); // Workaround strange KDevelop behaviour
0055         } else if (end.specialType() == KDevelop::VcsRevision::Working) {
0056             return QString();
0057         } else if (end.specialType() == KDevelop::VcsRevision::Start) {
0058             return QStringLiteral("-..r1");
0059         } else {
0060             return QString(); // Don't know how to handle this situation
0061         }
0062     } else if (end.revisionType() == KDevelop::VcsRevision::GlobalNumber) {
0063         return QStringLiteral("-r") + QString::number(end.revisionValue().toLongLong());
0064     }
0065 
0066     return QString();    // Don't know how to handle this situation
0067 }
0068 
0069 QString BazaarUtils::getRevisionSpecRange(const KDevelop::VcsRevision& begin,
0070                              const KDevelop::VcsRevision& end)
0071 {
0072     if (begin.revisionType() == KDevelop::VcsRevision::Special) {
0073         if (begin.specialType() == KDevelop::VcsRevision::Previous) {
0074             if (end.revisionType() == KDevelop::VcsRevision::Special) {
0075                 if (end.specialType() == KDevelop::VcsRevision::Base ||
0076                         end.specialType() == KDevelop::VcsRevision::Head)
0077                     return QStringLiteral("-rlast:2..last:1");
0078                 else if (end.specialType() == KDevelop::VcsRevision::Working)
0079                     return QString();
0080                 else if (end.specialType() == KDevelop::VcsRevision::Start)
0081                     return QStringLiteral("-r0..1");        // That's wrong revision range
0082             } else if (end.revisionType() == KDevelop::VcsRevision::GlobalNumber)
0083                 return QStringLiteral("-r") +
0084                        QString::number(end.revisionValue().toLongLong() - 1)
0085                        + QLatin1String("..") + QString::number(end.revisionValue().toLongLong());
0086             else
0087                 return QString(); // Don't know how to handle this situation
0088         } else if (begin.specialType() == KDevelop::VcsRevision::Base ||
0089                    begin.specialType() == KDevelop::VcsRevision::Head) {
0090             // Only one possibility: comparing working copy to last commit
0091             return QString();
0092         }
0093     } else if (begin.revisionType() == KDevelop::VcsRevision::GlobalNumber) {
0094         if (end.revisionType() == KDevelop::VcsRevision::Special) {
0095             // Assuming working copy
0096             return QLatin1String("-r") + QString::number(begin.revisionValue().toLongLong());
0097         } else {
0098             return QLatin1String("-r") + QString::number(begin.revisionValue().toLongLong())
0099                    + QLatin1String("..") + QString::number(end.revisionValue().toLongLong());
0100         }
0101     }
0102     return QString(); // Don't know how to handle this situation
0103 }
0104 
0105 bool BazaarUtils::isValidDirectory(const QUrl& dirPath)
0106 {
0107     QDir dir = BazaarUtils::workingCopy(dirPath);
0108 
0109     return dir.cd(QStringLiteral(".bzr")) && dir.exists(QStringLiteral("branch"));
0110 }
0111 
0112 KDevelop::VcsStatusInfo BazaarUtils::parseVcsStatusInfoLine(const QString& line)
0113 {
0114     const auto tokens = line.splitRef(QLatin1Char(' '), Qt::SkipEmptyParts);
0115     KDevelop::VcsStatusInfo result;
0116     if (tokens.size() < 2) // Don't know how to handle this situation (it is an error)
0117         return result;
0118     result.setUrl(QUrl::fromLocalFile(tokens.back().toString()));
0119     if (tokens[0] == QLatin1String("M")) {
0120         result.setState(KDevelop::VcsStatusInfo::ItemModified);
0121     } else if (tokens[0] == QLatin1String("C")) {
0122         result.setState(KDevelop::VcsStatusInfo::ItemHasConflicts);
0123     } else if (tokens[0] == QLatin1String("+N")) {
0124         result.setState(KDevelop::VcsStatusInfo::ItemAdded);
0125     } else if (tokens[0] == QLatin1String("?")) {
0126         result.setState(KDevelop::VcsStatusInfo::ItemUnknown);
0127     } else if (tokens[0] == QLatin1String("D")) {
0128         result.setState(KDevelop::VcsStatusInfo::ItemDeleted);
0129     } else {
0130         result.setState(KDevelop::VcsStatusInfo::ItemUserState);
0131         qWarning() << "Unsupported status: " << tokens[0];
0132     }
0133     return result;
0134 }
0135 
0136 QString BazaarUtils::concatenatePath(const QDir& workingCopy, const QUrl& pathInWorkingCopy)
0137 {
0138     return QFileInfo(workingCopy.absolutePath() + QDir::separator()
0139                      + pathInWorkingCopy.toLocalFile()).absoluteFilePath();
0140 }
0141 
0142 KDevelop::VcsEvent BazaarUtils::parseBzrLogPart(const QString& output)
0143 {
0144     const QStringList outputLines = output.split(QLatin1Char('\n'));
0145     KDevelop::VcsEvent commitInfo;
0146     bool atMessage = false;
0147     QString message;
0148     bool afterMessage = false;
0149     QHash<QString, KDevelop::VcsItemEvent::Actions> fileToActionsMapping;
0150     KDevelop::VcsItemEvent::Action currentAction;
0151     for (const QString &line : outputLines) {
0152         if (!atMessage) {
0153             if (line.startsWith(QLatin1String("revno"))) {
0154                 QString revno = line.mid(QStringLiteral("revno: ").length());
0155                 revno = revno.left(revno.indexOf(QLatin1Char(' ')));
0156                 KDevelop::VcsRevision revision;
0157                 revision.setRevisionValue(revno.toLongLong(), KDevelop::VcsRevision::GlobalNumber);
0158                 commitInfo.setRevision(revision);
0159             } else if (line.startsWith(QLatin1String("committer: "))) {
0160                 QString commiter = line.mid(QStringLiteral("committer: ").length());
0161                 commitInfo.setAuthor(commiter);     // Author goes after committer, but only if is different
0162             } else if (line.startsWith(QLatin1String("author"))) {
0163                 QString author = line.mid(QStringLiteral("author: ").length());
0164                 commitInfo.setAuthor(author);       // It may override committer (In fact committer is not supported by VcsEvent)
0165             } else if (line.startsWith(QLatin1String("timestamp"))) {
0166                 const QString formatString = QStringLiteral("yyyy-MM-dd hh:mm:ss");
0167                 QString timestamp = line.mid(QStringLiteral("timestamp: ddd ").length(), formatString.length());
0168                 commitInfo.setDate(QDateTime::fromString(timestamp, formatString));
0169             } else if (line.startsWith(QLatin1String("message"))) {
0170                 atMessage = true;
0171             }
0172         } else if (atMessage && !afterMessage) {
0173             if (!line.isEmpty() && line[0].isSpace()) {
0174                 message += line.trimmed() + QLatin1Char('\n');
0175             } else if (!line.isEmpty()) {
0176                 afterMessage = true;
0177                 // leave atMessage = true
0178                 currentAction = BazaarUtils::parseActionDescription(line);
0179             } // if line is empty - ignore and get next
0180         } else if (afterMessage) {
0181             if (!line.isEmpty() && !line[0].isSpace()) {
0182                 currentAction = BazaarUtils::parseActionDescription(line);
0183             } else if (!line.isEmpty()) {
0184                 fileToActionsMapping[line.trimmed()] |= currentAction;
0185             } // if line is empty - ignore and get next
0186         }
0187     }
0188     if (atMessage)
0189         commitInfo.setMessage(message.trimmed());
0190     for (auto i = fileToActionsMapping.begin(); i != fileToActionsMapping.end(); ++i) {
0191         KDevelop::VcsItemEvent itemEvent;
0192         itemEvent.setRepositoryLocation(i.key());
0193         itemEvent.setActions(i.value());
0194         commitInfo.addItem(itemEvent);
0195     }
0196     return commitInfo;
0197 }
0198 
0199 KDevelop::VcsItemEvent::Action BazaarUtils::parseActionDescription(const QString& action)
0200 {
0201     if (action == QLatin1String("added:")) {
0202         return KDevelop::VcsItemEvent::Added;
0203     } else if (action == QLatin1String("modified:")) {
0204         return KDevelop::VcsItemEvent::Modified;
0205     } else if (action == QLatin1String("removed:")) {
0206         return KDevelop::VcsItemEvent::Deleted;
0207     } else if (action == QLatin1String("kind changed:")) {
0208         return KDevelop::VcsItemEvent::Replaced; // Best approximation
0209     } else if (action.startsWith(QLatin1String("renamed"))) {
0210         return KDevelop::VcsItemEvent::Modified; // Best approximation
0211     } else {
0212         qCritical("Unsupported action: %s", action.toLocal8Bit().constData());
0213         return KDevelop::VcsItemEvent::Action();
0214     }
0215 }
0216 
0217 QList<QUrl> BazaarUtils::handleRecursion(const QList<QUrl>& listOfUrls, KDevelop::IBasicVersionControl::RecursionMode recursion)
0218 {
0219     if (recursion == KDevelop::IBasicVersionControl::Recursive) {
0220         return listOfUrls;      // Nothing to do
0221     } else {
0222         QList<QUrl> result;
0223         for (const auto& url : listOfUrls) {
0224             if (url.isLocalFile() && QFileInfo(url.toLocalFile()).isFile()) {
0225                 result.push_back(url);
0226             }
0227         }
0228         return result;
0229     }
0230 }