File indexing completed on 2024-05-12 04:38:56
0001 /* 0002 SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "vcsdiffpatchsources.h" 0008 0009 #include <QHBoxLayout> 0010 #include <QVBoxLayout> 0011 #include <QDir> 0012 #include <QFontDatabase> 0013 #include <QLabel> 0014 #include <QTemporaryFile> 0015 0016 #include <KComboBox> 0017 #include <KConfigGroup> 0018 #include <KLocalizedString> 0019 #include <KMessageBox> 0020 #include <KTextEdit> 0021 0022 #include <interfaces/ibasicversioncontrol.h> 0023 #include <interfaces/icore.h> 0024 #include <interfaces/iprojectcontroller.h> 0025 #include <interfaces/iplugincontroller.h> 0026 #include <interfaces/iruncontroller.h> 0027 #include <interfaces/isession.h> 0028 #include "vcsdiff.h" 0029 #include "vcsjob.h" 0030 #include "debug.h" 0031 0032 0033 using namespace KDevelop; 0034 0035 VCSCommitDiffPatchSource::VCSCommitDiffPatchSource(VCSDiffUpdater* updater) 0036 : VCSDiffPatchSource(updater), m_vcs(updater->vcs()) 0037 { 0038 Q_ASSERT(m_vcs); 0039 m_commitMessageWidget = new QWidget; 0040 auto* layout = new QVBoxLayout(m_commitMessageWidget.data()); 0041 layout->setContentsMargins(0, 0, 0, 0); 0042 0043 m_commitMessageEdit = new KTextEdit; 0044 m_commitMessageEdit.data()->setFont(QFontDatabase::systemFont(QFontDatabase::FixedFont)); 0045 m_commitMessageEdit.data()->setLineWrapMode(QTextEdit::NoWrap); 0046 m_vcs->setupCommitMessageEditor(updater->url(), m_commitMessageEdit.data()); 0047 0048 auto* titleLayout = new QHBoxLayout; 0049 titleLayout->addWidget(new QLabel(i18nc("@label:textbox", "Commit message:"))); 0050 0051 m_oldMessages = new KComboBox(m_commitMessageWidget.data()); 0052 0053 m_oldMessages->addItem(i18n("Old Messages")); 0054 const auto oldMessages = this->oldMessages(); 0055 for (const QString& message : oldMessages) { 0056 m_oldMessages->addItem(message, message); 0057 } 0058 m_oldMessages->setMaximumWidth(200); 0059 0060 connect(m_oldMessages, &QComboBox::currentTextChanged, 0061 this, &VCSCommitDiffPatchSource::oldMessageChanged); 0062 0063 titleLayout->addWidget(m_oldMessages); 0064 0065 layout->addLayout(titleLayout); 0066 layout->addWidget(m_commitMessageEdit.data()); 0067 connect(this, &VCSCommitDiffPatchSource::reviewCancelled, this, &VCSCommitDiffPatchSource::addMessageToHistory); 0068 connect(this, &VCSCommitDiffPatchSource::reviewFinished, this, &VCSCommitDiffPatchSource::addMessageToHistory); 0069 } 0070 0071 QStringList VCSCommitDiffPatchSource::oldMessages() const 0072 { 0073 KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); 0074 return vcsGroup.readEntry("OldCommitMessages", QStringList()); 0075 } 0076 0077 void VCSCommitDiffPatchSource::addMessageToHistory(const QString& message) 0078 { 0079 if(ICore::self()->shuttingDown()) 0080 return; 0081 0082 KConfigGroup vcsGroup(ICore::self()->activeSession()->config(), "VCS"); 0083 0084 const int maxMessages = 10; 0085 QStringList oldMessages = vcsGroup.readEntry("OldCommitMessages", QStringList()); 0086 0087 oldMessages.removeAll(message); 0088 oldMessages.push_front(message); 0089 oldMessages = oldMessages.mid(0, maxMessages); 0090 0091 vcsGroup.writeEntry("OldCommitMessages", oldMessages); 0092 } 0093 0094 void VCSCommitDiffPatchSource::oldMessageChanged(const QString& text) 0095 { 0096 if(m_oldMessages->currentIndex() != 0) 0097 { 0098 m_oldMessages->setCurrentIndex(0); 0099 m_commitMessageEdit.data()->setText(text); 0100 } 0101 } 0102 0103 void VCSCommitDiffPatchSource::jobFinished(KJob *job) 0104 { 0105 if (!job || job->error() != 0 ) 0106 { 0107 QString details = job ? job->errorText() : QString(); 0108 if (details.isEmpty()) { //errorText may be empty 0109 details = i18n("For more detailed information please see the Version Control tool view."); 0110 } 0111 KMessageBox::detailedError(nullptr, i18n("Unable to commit"), details, i18nc("@title:window", "Commit Unsuccessful")); 0112 } 0113 0114 deleteLater(); 0115 } 0116 0117 VCSDiffPatchSource::VCSDiffPatchSource(VCSDiffUpdater* updater) 0118 : m_updater(updater) 0119 { 0120 update(); 0121 KDevelop::IBasicVersionControl* vcs = m_updater->vcs(); 0122 QUrl url = m_updater->url(); 0123 0124 QScopedPointer<VcsJob> statusJob(vcs->status(QList<QUrl>() << url)); 0125 QVariant varlist; 0126 0127 if( statusJob->exec() && statusJob->status() == VcsJob::JobSucceeded ) 0128 { 0129 varlist = statusJob->fetchResults(); 0130 0131 const auto vars = varlist.toList(); 0132 m_infos.reserve(m_infos.size() + vars.size()); 0133 for (const auto& var : vars) { 0134 VcsStatusInfo info = var.value<KDevelop::VcsStatusInfo>(); 0135 0136 m_infos += info; 0137 if(info.state()!=VcsStatusInfo::ItemUpToDate) 0138 m_selectable[info.url()] = info.state(); 0139 } 0140 } 0141 else 0142 qCDebug(VCS) << "Couldn't get status for urls: " << url; 0143 } 0144 0145 VCSDiffPatchSource::VCSDiffPatchSource(const KDevelop::VcsDiff& diff) 0146 : m_updater(nullptr) 0147 { 0148 updateFromDiff(diff); 0149 } 0150 0151 VCSDiffPatchSource::~VCSDiffPatchSource() 0152 { 0153 QFile::remove(m_file.toLocalFile()); 0154 delete m_updater; 0155 } 0156 0157 QUrl VCSDiffPatchSource::baseDir() const { 0158 return m_base; 0159 } 0160 0161 QUrl VCSDiffPatchSource::file() const { 0162 return m_file; 0163 } 0164 0165 QString VCSDiffPatchSource::name() const { 0166 return m_name; 0167 } 0168 0169 uint VCSDiffPatchSource::depth() const { 0170 return m_depth; 0171 } 0172 0173 void VCSDiffPatchSource::updateFromDiff(const VcsDiff& vcsdiff) 0174 { 0175 if(!m_file.isValid()) 0176 { 0177 QTemporaryFile temp2(QDir::tempPath() + QLatin1String("/kdevelop_XXXXXX.patch")); 0178 temp2.setAutoRemove(false); 0179 temp2.open(); 0180 QTextStream t2(&temp2); 0181 t2 << vcsdiff.diff(); 0182 qCDebug(VCS) << "filename:" << temp2.fileName(); 0183 m_file = QUrl::fromLocalFile(temp2.fileName()); 0184 temp2.close(); 0185 }else{ 0186 QFile file(m_file.path()); 0187 file.open(QIODevice::WriteOnly); 0188 QTextStream t2(&file); 0189 t2 << vcsdiff.diff(); 0190 } 0191 0192 qCDebug(VCS) << "using file" << m_file << vcsdiff.diff() << "base" << vcsdiff.baseDiff(); 0193 0194 m_name = QStringLiteral("VCS Diff"); 0195 m_base = vcsdiff.baseDiff(); 0196 m_depth = vcsdiff.depth(); 0197 0198 emit patchChanged(); 0199 } 0200 0201 void VCSDiffPatchSource::update() { 0202 if(!m_updater) 0203 return; 0204 updateFromDiff(m_updater->update()); 0205 } 0206 0207 VCSCommitDiffPatchSource::~VCSCommitDiffPatchSource() { 0208 delete m_commitMessageWidget.data(); 0209 } 0210 0211 bool VCSCommitDiffPatchSource::canSelectFiles() const { 0212 return true; 0213 } 0214 0215 QMap< QUrl, KDevelop::VcsStatusInfo::State> VCSDiffPatchSource::additionalSelectableFiles() const { 0216 return m_selectable; 0217 } 0218 0219 QWidget* VCSCommitDiffPatchSource::customWidget() const { 0220 return m_commitMessageWidget.data(); 0221 } 0222 0223 QString VCSCommitDiffPatchSource::finishReviewCustomText() const { 0224 return i18nc("@action:button To make a commit", "Commit"); 0225 } 0226 0227 bool VCSCommitDiffPatchSource::canCancel() const { 0228 return true; 0229 } 0230 0231 void VCSCommitDiffPatchSource::cancelReview() { 0232 0233 QString message; 0234 0235 if (m_commitMessageEdit) 0236 message = m_commitMessageEdit.data()->toPlainText(); 0237 0238 emit reviewCancelled(message); 0239 0240 deleteLater(); 0241 } 0242 0243 bool VCSCommitDiffPatchSource::finishReview(const QList<QUrl>& selection) 0244 { 0245 QString message; 0246 0247 if (m_commitMessageEdit) 0248 message = m_commitMessageEdit.data()->toPlainText(); 0249 0250 qCDebug(VCS) << "Finishing with selection" << selection; 0251 QString files; 0252 files.reserve(selection.size()); 0253 for (const QUrl& url : selection) { 0254 files += QLatin1String("<li>") + ICore::self()->projectController()->prettyFileName(url, KDevelop::IProjectController::FormatPlain) + QLatin1String("</li>"); 0255 } 0256 0257 QString text = i18n("<qt>Files will be committed:\n<ul>%1</ul>\nWith message:\n <pre>%2</pre></qt>", files, message); 0258 0259 int res = KMessageBox::warningContinueCancel(nullptr, text, i18nc("@title:window", "About to Commit to Repository"), 0260 KStandardGuiItem::cont(), KStandardGuiItem::cancel(), 0261 QStringLiteral("ShouldAskConfirmCommit")); 0262 if (res != KMessageBox::Continue) { 0263 return false; 0264 } 0265 0266 emit reviewFinished(message, selection); 0267 0268 VcsJob* job = m_vcs->commit(message, selection, KDevelop::IBasicVersionControl::NonRecursive); 0269 if (!job) { 0270 return false; 0271 } 0272 0273 connect (job, &VcsJob::finished, 0274 this, &VCSCommitDiffPatchSource::jobFinished); 0275 ICore::self()->runController()->registerJob(job); 0276 return true; 0277 } 0278 0279 bool showVcsDiff(IPatchSource* vcsDiff) 0280 { 0281 auto* patchReview = ICore::self()->pluginController()->extensionForPlugin<IPatchReview>(QStringLiteral("org.kdevelop.IPatchReview")); 0282 0283 if( patchReview ) { 0284 patchReview->startReview(vcsDiff); 0285 return true; 0286 } else { 0287 qCWarning(VCS) << "Patch review plugin not found"; 0288 return false; 0289 } 0290 } 0291 0292 VcsDiff VCSStandardDiffUpdater::update() const 0293 { 0294 QScopedPointer<VcsJob> diffJob(m_vcs->diff(m_url, 0295 KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Base), 0296 KDevelop::VcsRevision::createSpecialRevision(KDevelop::VcsRevision::Working))); 0297 const bool success = diffJob ? diffJob->exec() : false; 0298 if (!success) { 0299 KMessageBox::error(nullptr, i18n("Could not create a patch for the current version.")); 0300 return {}; 0301 } 0302 0303 return diffJob->fetchResults().value<VcsDiff>(); 0304 } 0305 0306 VCSStandardDiffUpdater::VCSStandardDiffUpdater(IBasicVersionControl* vcs, const QUrl& url) 0307 : m_vcs(vcs) 0308 , m_url(url) 0309 { 0310 } 0311 0312 VCSStandardDiffUpdater::~VCSStandardDiffUpdater() { 0313 } 0314 0315 VCSDiffUpdater::~VCSDiffUpdater() { 0316 } 0317 0318 #include "moc_vcsdiffpatchsources.cpp"