File indexing completed on 2024-10-13 08:09:17
0001 // SPDX-FileCopyrightText: 2021 Simon Persson <simon.persson@mykolab.com> 0002 // 0003 // SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL 0004 0005 #include "purger.h" 0006 #include "kuppurger_debug.h" 0007 0008 #include <KFormat> 0009 #include <KLocalizedString> 0010 #include <KStandardAction> 0011 #include <KToolBar> 0012 0013 #include <QDateTime> 0014 #include <QGuiApplication> 0015 #include <QHash> 0016 #include <QSplitter> 0017 0018 Purger::Purger(QString pRepoPath, QString pBranchName, QWidget *pParent) 0019 : KMainWindow(pParent), mRepoPath(std::move(pRepoPath)), 0020 mBranchName(std::move(pBranchName)) 0021 { 0022 setWindowIcon(QIcon::fromTheme(QStringLiteral("kup"))); 0023 KToolBar *lAppToolBar = toolBar(); 0024 lAppToolBar->addAction(KStandardAction::quit(this, SLOT(close()), this)); 0025 mDeleteAction = KStandardAction::deleteFile(this, SLOT(purge()), this); 0026 lAppToolBar->addAction(mDeleteAction); 0027 0028 mListWidget = new QListWidget(); 0029 auto lSplitter = new QSplitter(); 0030 lSplitter->addWidget(mListWidget); 0031 mTextEdit = new QTextEdit(); 0032 mTextEdit->setReadOnly(true); 0033 lSplitter->addWidget(mTextEdit); 0034 setCentralWidget(lSplitter); 0035 0036 mCollectProcess = new KProcess(); 0037 mCollectProcess->setOutputChannelMode(KProcess::SeparateChannels); 0038 *mCollectProcess << QStringLiteral("bup"); 0039 *mCollectProcess << QStringLiteral("-d") << mRepoPath; 0040 *mCollectProcess << QStringLiteral("gc") << QStringLiteral("--unsafe") << QStringLiteral("--verbose"); 0041 connect(mCollectProcess, &KProcess::readyReadStandardError, this, [this] { 0042 auto lLogText = QString::fromUtf8(mCollectProcess->readAllStandardError()); 0043 qCInfo(KUPPURGER) << lLogText; 0044 mTextEdit->append(lLogText); 0045 }); 0046 connect(mCollectProcess, SIGNAL(finished(int,QProcess::ExitStatus)), 0047 SLOT(purgeDone(int,QProcess::ExitStatus))); 0048 0049 mListProcess = new KProcess(); 0050 mListProcess->setOutputChannelMode(KProcess::SeparateChannels); 0051 *mListProcess << QStringLiteral("bup"); 0052 *mListProcess << QStringLiteral("-d") << mRepoPath; 0053 *mListProcess << QStringLiteral("ls") << QStringLiteral("--hash") << mBranchName; 0054 connect(mListProcess, &KProcess::readyReadStandardOutput, this, [this] { 0055 KFormat lFormat; 0056 const auto lLines = QString::fromUtf8(mListProcess->readAllStandardOutput()).split(QChar::LineFeed); 0057 for(const QString &lLine: lLines) { 0058 qCDebug(KUPPURGER) << lLine; 0059 const auto lHash = lLine.left(40); 0060 if(!lHash.isEmpty() && lHash != QStringLiteral("0000000000000000000000000000000000000000")) { 0061 const auto lTimeStamp = lLine.mid(41); 0062 if(mHashes.contains(lHash)) { 0063 auto lItem = mHashes.value(lHash); 0064 lItem->setWhatsThis(lItem->whatsThis() + QChar::LineFeed + lTimeStamp); 0065 } else { 0066 const auto lDateTime = QDateTime::fromString(lTimeStamp, QStringLiteral("yyyy-MM-dd-HHmmss")); 0067 const auto lDisplayText = lFormat.formatRelativeDateTime(lDateTime, QLocale::ShortFormat); 0068 auto lItem = new QListWidgetItem(lDisplayText, mListWidget); 0069 lItem->setWhatsThis(lTimeStamp); //misuse of field, for later use when removing 0070 lItem->setFlags(Qt::ItemIsUserCheckable | Qt::ItemIsEnabled); 0071 lItem->setCheckState(Qt::Unchecked); 0072 mHashes.insert(lHash, lItem); 0073 } 0074 } 0075 } 0076 }); 0077 connect(mListProcess, SIGNAL(finished(int,QProcess::ExitStatus)), 0078 SLOT(listDone(int,QProcess::ExitStatus))); 0079 0080 fillListWidget(); 0081 } 0082 0083 QSize Purger::sizeHint() const { 0084 return {800, 600}; 0085 } 0086 0087 void Purger::fillListWidget() { 0088 QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0089 mListWidget->clear(); 0090 mHashes.clear(); 0091 mDeleteAction->setEnabled(false); 0092 mListProcess->start(); 0093 } 0094 0095 void Purger::listDone(int, QProcess::ExitStatus) { 0096 QGuiApplication::restoreOverrideCursor(); 0097 mDeleteAction->setEnabled(true); 0098 } 0099 0100 void Purger::purge() { 0101 qCInfo(KUPPURGER) << "Starting purge operation."; 0102 mDeleteAction->setEnabled(false); 0103 bool lAnythingRemoved = false; 0104 for(int i=0; i < mListWidget->count(); ++i) { 0105 auto lItem = mListWidget->item(i); 0106 qCInfo(KUPPURGER) << lItem->text() << lItem->whatsThis() << lItem->checkState(); 0107 if(lItem->checkState() == Qt::Checked) { 0108 const auto lTimeStamps = lItem->whatsThis().split(QChar::LineFeed); 0109 for(const QString &lTimeStamp: lTimeStamps) { 0110 KProcess lRemoveProcess; 0111 lRemoveProcess.setOutputChannelMode(KProcess::SeparateChannels); 0112 lRemoveProcess << QStringLiteral("bup"); 0113 lRemoveProcess << QStringLiteral("-d") << mRepoPath << QStringLiteral("rm"); 0114 lRemoveProcess << QStringLiteral("--unsafe") << QStringLiteral("--verbose"); 0115 lRemoveProcess << QString("%1/%2").arg(mBranchName, lTimeStamp); 0116 qCInfo(KUPPURGER) << lRemoveProcess.program(); 0117 if(lRemoveProcess.execute() == 0) { 0118 lAnythingRemoved = true; 0119 qCInfo(KUPPURGER) << "Successfully removed snapshot"; 0120 } 0121 const auto lLogText = QString::fromUtf8(lRemoveProcess.readAllStandardError()); 0122 qCInfo(KUPPURGER) << lLogText; 0123 mTextEdit->append(lLogText); 0124 } 0125 } 0126 } 0127 if(lAnythingRemoved) { 0128 QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); 0129 qCInfo(KUPPURGER) << mCollectProcess->program(); 0130 mCollectProcess->start(); 0131 } else { 0132 mDeleteAction->setEnabled(true); 0133 } 0134 } 0135 0136 void Purger::purgeDone(int pExitCode, QProcess::ExitStatus pExitStatus) { 0137 qCInfo(KUPPURGER) << pExitCode << pExitStatus; 0138 QGuiApplication::restoreOverrideCursor(); 0139 mDeleteAction->setEnabled(true); 0140 const auto lLogText = QString::fromUtf8(mCollectProcess->readAllStandardError()); 0141 qCInfo(KUPPURGER) << lLogText; 0142 mTextEdit->append(lLogText); 0143 fillListWidget(); 0144 } 0145