1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Brian McGillion and Hugues Delorme
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25 
26 #include "vcsbaseclient.h"
27 #include "vcscommand.h"
28 #include "vcsbaseclientsettings.h"
29 #include "vcsbaseeditorconfig.h"
30 
31 #include <coreplugin/icore.h>
32 #include <coreplugin/vcsmanager.h>
33 #include <coreplugin/editormanager/documentmodel.h>
34 #include <coreplugin/editormanager/editormanager.h>
35 #include <coreplugin/idocument.h>
36 
37 #include <utils/qtcassert.h>
38 #include <utils/qtcprocess.h>
39 
40 #include <vcsbase/vcsbaseeditor.h>
41 #include <vcsbase/vcsoutputwindow.h>
42 #include <vcsbase/vcsbaseplugin.h>
43 
44 #include <QStringList>
45 #include <QDir>
46 #include <QProcess>
47 #include <QTextCodec>
48 #include <QDebug>
49 #include <QFileInfo>
50 #include <QByteArray>
51 #include <QVariant>
52 #include <QProcessEnvironment>
53 
54 using namespace Utils;
55 
56 /*!
57     \class VcsBase::VcsBaseClient
58 
59     \brief The VcsBaseClient class is the base class for Mercurial and Bazaar
60     'clients'.
61 
62     Provides base functionality for common commands (diff, log, etc).
63 
64     \sa VcsBase::VcsJobRunner
65 */
66 
locateEditor(const char * property,const QString & entry)67 static Core::IEditor *locateEditor(const char *property, const QString &entry)
68 {
69     foreach (Core::IDocument *document, Core::DocumentModel::openedDocuments())
70         if (document->property(property).toString() == entry)
71             return Core::DocumentModel::editorsForDocument(document).constFirst();
72     return nullptr;
73 }
74 
75 namespace VcsBase {
76 
VcsBaseClientImpl(VcsBaseSettings * baseSettings)77 VcsBaseClientImpl::VcsBaseClientImpl(VcsBaseSettings *baseSettings)
78     : m_baseSettings(baseSettings)
79 {
80     m_baseSettings->readSettings(Core::ICore::settings());
81     connect(Core::ICore::instance(), &Core::ICore::saveSettingsRequested,
82             this, &VcsBaseClientImpl::saveSettings);
83 }
84 
settings() const85 VcsBaseSettings &VcsBaseClientImpl::settings() const
86 {
87     return *m_baseSettings;
88 }
89 
vcsBinary() const90 FilePath VcsBaseClientImpl::vcsBinary() const
91 {
92     return m_baseSettings->binaryPath.filePath();
93 }
94 
createCommand(const QString & workingDirectory,VcsBaseEditorWidget * editor,JobOutputBindMode mode) const95 VcsCommand *VcsBaseClientImpl::createCommand(const QString &workingDirectory,
96                                              VcsBaseEditorWidget *editor,
97                                              JobOutputBindMode mode) const
98 {
99     auto cmd = new VcsCommand(workingDirectory, processEnvironment());
100     cmd->setDefaultTimeoutS(vcsTimeoutS());
101     if (editor)
102         editor->setCommand(cmd);
103     if (mode == VcsWindowOutputBind) {
104         cmd->addFlags(VcsCommand::ShowStdOut);
105         if (editor) // assume that the commands output is the important thing
106             cmd->addFlags(VcsCommand::SilentOutput);
107     } else if (editor) {
108         connect(cmd, &VcsCommand::stdOutText, editor, &VcsBaseEditorWidget::setPlainText);
109     }
110 
111     return cmd;
112 }
113 
enqueueJob(VcsCommand * cmd,const QStringList & args,const QString & workingDirectory,const ExitCodeInterpreter & interpreter) const114 void VcsBaseClientImpl::enqueueJob(VcsCommand *cmd, const QStringList &args,
115                                    const QString &workingDirectory,
116                                    const ExitCodeInterpreter &interpreter) const
117 {
118     cmd->addJob({vcsBinary(), args}, vcsTimeoutS(), workingDirectory, interpreter);
119     cmd->execute();
120 }
121 
processEnvironment() const122 Environment VcsBaseClientImpl::processEnvironment() const
123 {
124     Environment environment = Environment::systemEnvironment();
125     VcsBase::setProcessEnvironment(&environment, false);
126     return environment;
127 }
128 
splitLines(const QString & s)129 QStringList VcsBaseClientImpl::splitLines(const QString &s)
130 {
131     const QChar newLine = QLatin1Char('\n');
132     QString output = s;
133     if (output.endsWith(newLine))
134         output.truncate(output.size() - 1);
135     if (output.isEmpty())
136         return QStringList();
137     return output.split(newLine);
138 }
139 
stripLastNewline(const QString & in)140 QString VcsBaseClientImpl::stripLastNewline(const QString &in)
141 {
142     if (in.endsWith('\n'))
143         return in.left(in.count() - 1);
144     return in;
145 }
146 
vcsFullySynchronousExec(QtcProcess & proc,const QString & workingDir,const CommandLine & cmdLine,unsigned flags,int timeoutS,QTextCodec * codec) const147 void VcsBaseClientImpl::vcsFullySynchronousExec(QtcProcess &proc,
148                                                 const QString &workingDir, const CommandLine &cmdLine,
149                                                 unsigned flags, int timeoutS, QTextCodec *codec) const
150 {
151     VcsCommand command(workingDir, processEnvironment());
152     command.addFlags(flags);
153     if (codec)
154         command.setCodec(codec);
155     proc.setTimeoutS(timeoutS > 0 ? timeoutS : vcsTimeoutS());
156     command.runCommand(proc, cmdLine);
157 }
158 
resetCachedVcsInfo(const QString & workingDir)159 void VcsBaseClientImpl::resetCachedVcsInfo(const QString &workingDir)
160 {
161     Core::VcsManager::resetVersionControlForDirectory(workingDir);
162 }
163 
annotateRevisionRequested(const QString & workingDirectory,const QString & file,const QString & change,int line)164 void VcsBaseClientImpl::annotateRevisionRequested(const QString &workingDirectory,
165                                                   const QString &file, const QString &change,
166                                                   int line)
167 {
168     QString changeCopy = change;
169     // This might be invoked with a verbose revision description
170     // "SHA1 author subject" from the annotation context menu. Strip the rest.
171     const int blankPos = changeCopy.indexOf(QLatin1Char(' '));
172     if (blankPos != -1)
173         changeCopy.truncate(blankPos);
174     annotate(workingDirectory, file, changeCopy, line);
175 }
176 
vcsFullySynchronousExec(QtcProcess & proc,const QString & workingDir,const QStringList & args,unsigned flags,int timeoutS,QTextCodec * codec) const177 void VcsBaseClientImpl::vcsFullySynchronousExec(QtcProcess &proc,
178                                                 const QString &workingDir, const QStringList &args,
179                                                 unsigned flags, int timeoutS, QTextCodec *codec) const
180 {
181     vcsFullySynchronousExec(proc, workingDir, {vcsBinary(), args}, flags, timeoutS, codec);
182 }
183 
vcsExec(const QString & workingDirectory,const QStringList & arguments,VcsBaseEditorWidget * editor,bool useOutputToWindow,unsigned additionalFlags,const QVariant & cookie) const184 VcsCommand *VcsBaseClientImpl::vcsExec(const QString &workingDirectory, const QStringList &arguments,
185                                        VcsBaseEditorWidget *editor, bool useOutputToWindow,
186                                        unsigned additionalFlags, const QVariant &cookie) const
187 {
188     VcsCommand *command = createCommand(workingDirectory, editor,
189                                         useOutputToWindow ? VcsWindowOutputBind : NoOutputBind);
190     command->setCookie(cookie);
191     command->addFlags(additionalFlags);
192     if (editor)
193         command->setCodec(editor->codec());
194     enqueueJob(command, arguments);
195     return command;
196 }
197 
vcsSynchronousExec(QtcProcess & proc,const QString & workingDir,const QStringList & args,unsigned flags,QTextCodec * outputCodec) const198 void VcsBaseClientImpl::vcsSynchronousExec(QtcProcess &proc, const QString &workingDir,
199                                            const QStringList &args,
200                                            unsigned flags,
201                                            QTextCodec *outputCodec) const
202 {
203     Environment env = processEnvironment();
204     VcsCommand command(workingDir, env.size() == 0 ? Environment::systemEnvironment() : env);
205     proc.setTimeoutS(vcsTimeoutS());
206     command.addFlags(flags);
207     command.setCodec(outputCodec);
208     command.runCommand(proc, {vcsBinary(), args});
209 }
210 
vcsTimeoutS() const211 int VcsBaseClientImpl::vcsTimeoutS() const
212 {
213     return m_baseSettings->timeout.value();
214 }
215 
createVcsEditor(Utils::Id kind,QString title,const QString & source,QTextCodec * codec,const char * registerDynamicProperty,const QString & dynamicPropertyValue) const216 VcsBaseEditorWidget *VcsBaseClientImpl::createVcsEditor(Utils::Id kind, QString title,
217                                                         const QString &source, QTextCodec *codec,
218                                                         const char *registerDynamicProperty,
219                                                         const QString &dynamicPropertyValue) const
220 {
221     VcsBaseEditorWidget *baseEditor = nullptr;
222     Core::IEditor *outputEditor = locateEditor(registerDynamicProperty, dynamicPropertyValue);
223     const QString progressMsg = tr("Working...");
224     if (outputEditor) {
225         // Exists already
226         outputEditor->document()->setContents(progressMsg.toUtf8());
227         baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
228         QTC_ASSERT(baseEditor, return nullptr);
229         Core::EditorManager::activateEditor(outputEditor);
230     } else {
231         outputEditor = Core::EditorManager::openEditorWithContents(kind, &title, progressMsg.toUtf8());
232         outputEditor->document()->setProperty(registerDynamicProperty, dynamicPropertyValue);
233         baseEditor = VcsBaseEditor::getVcsBaseEditor(outputEditor);
234         QTC_ASSERT(baseEditor, return nullptr);
235         connect(baseEditor, &VcsBaseEditorWidget::annotateRevisionRequested,
236                 this, &VcsBaseClientImpl::annotateRevisionRequested);
237         baseEditor->setSource(source);
238         if (codec)
239             baseEditor->setCodec(codec);
240     }
241 
242     baseEditor->setForceReadOnly(true);
243     return baseEditor;
244 }
245 
saveSettings()246 void VcsBaseClientImpl::saveSettings()
247 {
248     m_baseSettings->writeSettings(Core::ICore::settings());
249 }
250 
VcsBaseClient(VcsBaseSettings * baseSettings)251 VcsBaseClient::VcsBaseClient(VcsBaseSettings *baseSettings)
252     : VcsBaseClientImpl(baseSettings)
253 {
254     qRegisterMetaType<QVariant>();
255 }
256 
synchronousCreateRepository(const QString & workingDirectory,const QStringList & extraOptions)257 bool VcsBaseClient::synchronousCreateRepository(const QString &workingDirectory,
258                                                 const QStringList &extraOptions)
259 {
260     QStringList args(vcsCommandString(CreateRepositoryCommand));
261     args << extraOptions;
262     QtcProcess proc;
263     vcsFullySynchronousExec(proc, workingDirectory, args);
264     if (proc.result() != QtcProcess::FinishedWithSuccess)
265         return false;
266     VcsOutputWindow::append(proc.stdOut());
267 
268     resetCachedVcsInfo(workingDirectory);
269 
270     return true;
271 }
272 
synchronousClone(const QString & workingDir,const QString & srcLocation,const QString & dstLocation,const QStringList & extraOptions)273 bool VcsBaseClient::synchronousClone(const QString &workingDir,
274                                      const QString &srcLocation,
275                                      const QString &dstLocation,
276                                      const QStringList &extraOptions)
277 {
278     QStringList args;
279     args << vcsCommandString(CloneCommand)
280          << extraOptions << srcLocation << dstLocation;
281 
282     QtcProcess proc;
283     vcsFullySynchronousExec(proc, workingDir, args);
284     resetCachedVcsInfo(workingDir);
285     return proc.result() == QtcProcess::FinishedWithSuccess;
286 }
287 
synchronousAdd(const QString & workingDir,const QString & filename,const QStringList & extraOptions)288 bool VcsBaseClient::synchronousAdd(const QString &workingDir, const QString &filename,
289                                    const QStringList &extraOptions)
290 {
291     QStringList args;
292     args << vcsCommandString(AddCommand) << extraOptions << filename;
293     QtcProcess proc;
294     vcsFullySynchronousExec(proc, workingDir, args);
295     return proc.result() == QtcProcess::FinishedWithSuccess;
296 }
297 
synchronousRemove(const QString & workingDir,const QString & filename,const QStringList & extraOptions)298 bool VcsBaseClient::synchronousRemove(const QString &workingDir, const QString &filename,
299                                       const QStringList &extraOptions)
300 {
301     QStringList args;
302     args << vcsCommandString(RemoveCommand) << extraOptions << filename;
303     QtcProcess proc;
304     vcsFullySynchronousExec(proc, workingDir, args);
305     return proc.result() == QtcProcess::FinishedWithSuccess;
306 }
307 
synchronousMove(const QString & workingDir,const QString & from,const QString & to,const QStringList & extraOptions)308 bool VcsBaseClient::synchronousMove(const QString &workingDir,
309                                     const QString &from, const QString &to,
310                                     const QStringList &extraOptions)
311 {
312     QStringList args;
313     args << vcsCommandString(MoveCommand) << extraOptions << from << to;
314     QtcProcess proc;
315     vcsFullySynchronousExec(proc, workingDir, args);
316     return proc.result() == QtcProcess::FinishedWithSuccess;
317 }
318 
synchronousPull(const QString & workingDir,const QString & srcLocation,const QStringList & extraOptions)319 bool VcsBaseClient::synchronousPull(const QString &workingDir,
320                                     const QString &srcLocation,
321                                     const QStringList &extraOptions)
322 {
323     QStringList args;
324     args << vcsCommandString(PullCommand) << extraOptions << srcLocation;
325     // Disable UNIX terminals to suppress SSH prompting
326     const unsigned flags =
327             VcsCommand::SshPasswordPrompt
328             | VcsCommand::ShowStdOut
329             | VcsCommand::ShowSuccessMessage;
330     QtcProcess proc;
331     vcsSynchronousExec(proc, workingDir, args, flags);
332     const bool ok = proc.result() == QtcProcess::FinishedWithSuccess;
333     if (ok)
334         emit changed(QVariant(workingDir));
335     return ok;
336 }
337 
synchronousPush(const QString & workingDir,const QString & dstLocation,const QStringList & extraOptions)338 bool VcsBaseClient::synchronousPush(const QString &workingDir,
339                                     const QString &dstLocation,
340                                     const QStringList &extraOptions)
341 {
342     QStringList args;
343     args << vcsCommandString(PushCommand) << extraOptions << dstLocation;
344     // Disable UNIX terminals to suppress SSH prompting
345     const unsigned flags =
346             VcsCommand::SshPasswordPrompt
347             | VcsCommand::ShowStdOut
348             | VcsCommand::ShowSuccessMessage;
349     QtcProcess proc;
350     vcsSynchronousExec(proc, workingDir, args, flags);
351     return proc.result() == QtcProcess::FinishedWithSuccess;
352 }
353 
annotate(const QString & workingDir,const QString & file,const QString & revision,int lineNumber,const QStringList & extraOptions)354 VcsBaseEditorWidget *VcsBaseClient::annotate(
355         const QString &workingDir, const QString &file, const QString &revision /* = QString() */,
356         int lineNumber /* = -1 */, const QStringList &extraOptions)
357 {
358     const QString vcsCmdString = vcsCommandString(AnnotateCommand);
359     QStringList args;
360     args << vcsCmdString << revisionSpec(revision) << extraOptions << file;
361     const Utils::Id kind = vcsEditorKind(AnnotateCommand);
362     const QString id = VcsBaseEditor::getSource(workingDir, QStringList(file));
363     const QString title = vcsEditorTitle(vcsCmdString, id);
364     const QString source = VcsBaseEditor::getSource(workingDir, file);
365 
366     VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
367                                                   VcsBaseEditor::getCodec(source),
368                                                   vcsCmdString.toLatin1().constData(), id);
369 
370     VcsCommand *cmd = createCommand(workingDir, editor);
371     cmd->setCookie(lineNumber);
372     enqueueJob(cmd, args);
373     return editor;
374 }
375 
diff(const QString & workingDir,const QStringList & files,const QStringList & extraOptions)376 void VcsBaseClient::diff(const QString &workingDir, const QStringList &files,
377                          const QStringList &extraOptions)
378 {
379     const QString vcsCmdString = vcsCommandString(DiffCommand);
380     const Utils::Id kind = vcsEditorKind(DiffCommand);
381     const QString id = VcsBaseEditor::getTitleId(workingDir, files);
382     const QString title = vcsEditorTitle(vcsCmdString, id);
383     const QString source = VcsBaseEditor::getSource(workingDir, files);
384     VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
385                                                   VcsBaseEditor::getCodec(source),
386                                                   vcsCmdString.toLatin1().constData(), id);
387     editor->setWorkingDirectory(workingDir);
388 
389     VcsBaseEditorConfig *paramWidget = editor->editorConfig();
390     if (!paramWidget) {
391         if (m_diffConfigCreator)
392             paramWidget = m_diffConfigCreator(editor->toolBar());
393         if (paramWidget) {
394             paramWidget->setBaseArguments(extraOptions);
395             // editor has been just created, createVcsEditor() didn't set a configuration widget yet
396             connect(editor, &VcsBaseEditorWidget::diffChunkReverted,
397                     paramWidget, &VcsBaseEditorConfig::executeCommand);
398             connect(paramWidget, &VcsBaseEditorConfig::commandExecutionRequested,
399                 [=] { diff(workingDir, files, extraOptions); } );
400             editor->setEditorConfig(paramWidget);
401         }
402     }
403 
404     QStringList args = {vcsCmdString};
405     if (paramWidget)
406         args << paramWidget->arguments();
407     else
408         args << extraOptions;
409     args << files;
410     QTextCodec *codec = source.isEmpty() ? static_cast<QTextCodec *>(nullptr)
411                                          : VcsBaseEditor::getCodec(source);
412     VcsCommand *command = createCommand(workingDir, editor);
413     command->setCodec(codec);
414     enqueueJob(command, args, workingDir, exitCodeInterpreter(DiffCommand));
415 }
416 
log(const QString & workingDir,const QStringList & files,const QStringList & extraOptions,bool enableAnnotationContextMenu)417 void VcsBaseClient::log(const QString &workingDir, const QStringList &files,
418                         const QStringList &extraOptions,
419                         bool enableAnnotationContextMenu)
420 {
421     const QString vcsCmdString = vcsCommandString(LogCommand);
422     const Utils::Id kind = vcsEditorKind(LogCommand);
423     const QString id = VcsBaseEditor::getTitleId(workingDir, files);
424     const QString title = vcsEditorTitle(vcsCmdString, id);
425     const QString source = VcsBaseEditor::getSource(workingDir, files);
426     VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
427                                                   VcsBaseEditor::getCodec(source),
428                                                   vcsCmdString.toLatin1().constData(), id);
429     editor->setFileLogAnnotateEnabled(enableAnnotationContextMenu);
430 
431     VcsBaseEditorConfig *paramWidget = editor->editorConfig();
432     if (!paramWidget) {
433         if (m_logConfigCreator)
434             paramWidget = m_logConfigCreator(editor->toolBar());
435         if (paramWidget) {
436             paramWidget->setBaseArguments(extraOptions);
437             // editor has been just created, createVcsEditor() didn't set a configuration widget yet
438             connect(paramWidget, &VcsBaseEditorConfig::commandExecutionRequested,
439                 [=] { this->log(workingDir, files, extraOptions, enableAnnotationContextMenu); } );
440             editor->setEditorConfig(paramWidget);
441         }
442     }
443 
444     QStringList args = {vcsCmdString};
445     if (paramWidget)
446         args << paramWidget->arguments();
447     else
448         args << extraOptions;
449     args << files;
450     enqueueJob(createCommand(workingDir, editor), args);
451 }
452 
revertFile(const QString & workingDir,const QString & file,const QString & revision,const QStringList & extraOptions)453 void VcsBaseClient::revertFile(const QString &workingDir,
454                                const QString &file,
455                                const QString &revision,
456                                const QStringList &extraOptions)
457 {
458     QStringList args(vcsCommandString(RevertCommand));
459     args << revisionSpec(revision) << extraOptions << file;
460     // Indicate repository change or file list
461     VcsCommand *cmd = createCommand(workingDir);
462     cmd->setCookie(QStringList(workingDir + QLatin1Char('/') + file));
463     connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
464     enqueueJob(cmd, args);
465 }
466 
revertAll(const QString & workingDir,const QString & revision,const QStringList & extraOptions)467 void VcsBaseClient::revertAll(const QString &workingDir, const QString &revision,
468                               const QStringList &extraOptions)
469 {
470     QStringList args(vcsCommandString(RevertCommand));
471     args << revisionSpec(revision) << extraOptions;
472     // Indicate repository change or file list
473     VcsCommand *cmd = createCommand(workingDir);
474     cmd->setCookie(QStringList(workingDir));
475     connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
476     enqueueJob(createCommand(workingDir), args);
477 }
478 
status(const QString & workingDir,const QString & file,const QStringList & extraOptions)479 void VcsBaseClient::status(const QString &workingDir, const QString &file,
480                            const QStringList &extraOptions)
481 {
482     QStringList args(vcsCommandString(StatusCommand));
483     args << extraOptions << file;
484     VcsOutputWindow::setRepository(workingDir);
485     VcsCommand *cmd = createCommand(workingDir, nullptr, VcsWindowOutputBind);
486     connect(cmd, &VcsCommand::finished,
487             VcsOutputWindow::instance(), &VcsOutputWindow::clearRepository,
488             Qt::QueuedConnection);
489     enqueueJob(cmd, args);
490 }
491 
emitParsedStatus(const QString & repository,const QStringList & extraOptions)492 void VcsBaseClient::emitParsedStatus(const QString &repository, const QStringList &extraOptions)
493 {
494     QStringList args(vcsCommandString(StatusCommand));
495     args << extraOptions;
496     VcsCommand *cmd = createCommand(repository);
497     connect(cmd, &VcsCommand::stdOutText, this, &VcsBaseClient::statusParser);
498     enqueueJob(cmd, args);
499 }
500 
vcsCommandString(VcsCommandTag cmd) const501 QString VcsBaseClient::vcsCommandString(VcsCommandTag cmd) const
502 {
503     switch (cmd) {
504     case CreateRepositoryCommand: return QLatin1String("init");
505     case CloneCommand: return QLatin1String("clone");
506     case AddCommand: return QLatin1String("add");
507     case RemoveCommand: return QLatin1String("remove");
508     case MoveCommand: return QLatin1String("rename");
509     case PullCommand: return QLatin1String("pull");
510     case PushCommand: return QLatin1String("push");
511     case CommitCommand: return QLatin1String("commit");
512     case ImportCommand: return QLatin1String("import");
513     case UpdateCommand: return QLatin1String("update");
514     case RevertCommand: return QLatin1String("revert");
515     case AnnotateCommand: return QLatin1String("annotate");
516     case DiffCommand: return QLatin1String("diff");
517     case LogCommand: return QLatin1String("log");
518     case StatusCommand: return QLatin1String("status");
519     }
520     return QString();
521 }
522 
exitCodeInterpreter(VcsCommandTag cmd) const523 ExitCodeInterpreter VcsBaseClient::exitCodeInterpreter(VcsCommandTag cmd) const
524 {
525     Q_UNUSED(cmd)
526     return {};
527 }
528 
setDiffConfigCreator(ConfigCreator creator)529 void VcsBaseClient::setDiffConfigCreator(ConfigCreator creator)
530 {
531     m_diffConfigCreator = std::move(creator);
532 }
533 
setLogConfigCreator(ConfigCreator creator)534 void VcsBaseClient::setLogConfigCreator(ConfigCreator creator)
535 {
536     m_logConfigCreator = std::move(creator);
537 }
538 
import(const QString & repositoryRoot,const QStringList & files,const QStringList & extraOptions)539 void VcsBaseClient::import(const QString &repositoryRoot, const QStringList &files,
540                            const QStringList &extraOptions)
541 {
542     QStringList args(vcsCommandString(ImportCommand));
543     args << extraOptions << files;
544     enqueueJob(createCommand(repositoryRoot), args);
545 }
546 
view(const QString & source,const QString & id,const QStringList & extraOptions)547 void VcsBaseClient::view(const QString &source, const QString &id,
548                          const QStringList &extraOptions)
549 {
550     QStringList args;
551     args << extraOptions << revisionSpec(id);
552     const Utils::Id kind = vcsEditorKind(DiffCommand);
553     const QString title = vcsEditorTitle(vcsCommandString(LogCommand), id);
554 
555     VcsBaseEditorWidget *editor = createVcsEditor(kind, title, source,
556                                                   VcsBaseEditor::getCodec(source), "view", id);
557 
558     const QFileInfo fi(source);
559     const QString workingDirPath = fi.isFile() ? fi.absolutePath() : source;
560     enqueueJob(createCommand(workingDirPath, editor), args);
561 }
562 
update(const QString & repositoryRoot,const QString & revision,const QStringList & extraOptions)563 void VcsBaseClient::update(const QString &repositoryRoot, const QString &revision,
564                            const QStringList &extraOptions)
565 {
566     QStringList args(vcsCommandString(UpdateCommand));
567     args << revisionSpec(revision) << extraOptions;
568     VcsCommand *cmd = createCommand(repositoryRoot);
569     cmd->setCookie(repositoryRoot);
570     connect(cmd, &VcsCommand::success, this, &VcsBaseClient::changed, Qt::QueuedConnection);
571     enqueueJob(cmd, args);
572 }
573 
commit(const QString & repositoryRoot,const QStringList & files,const QString & commitMessageFile,const QStringList & extraOptions)574 void VcsBaseClient::commit(const QString &repositoryRoot,
575                            const QStringList &files,
576                            const QString &commitMessageFile,
577                            const QStringList &extraOptions)
578 {
579     // Handling of commitMessageFile is a bit tricky :
580     //   VcsBaseClient cannot do something with it because it doesn't know which
581     //   option to use (-F ? but sub VCS clients might require a different option
582     //   name like -l for hg ...)
583     //
584     //   So descendants of VcsBaseClient *must* redefine commit() and extend
585     //   extraOptions with the usage for commitMessageFile (see BazaarClient::commit()
586     //   for example)
587     QStringList args(vcsCommandString(CommitCommand));
588     args << extraOptions << files;
589     VcsCommand *cmd = createCommand(repositoryRoot, nullptr, VcsWindowOutputBind);
590     if (!commitMessageFile.isEmpty())
591         connect(cmd, &VcsCommand::finished, [commitMessageFile]() { QFile(commitMessageFile).remove(); });
592     enqueueJob(cmd, args);
593 }
594 
vcsEditorTitle(const QString & vcsCmd,const QString & sourceId) const595 QString VcsBaseClient::vcsEditorTitle(const QString &vcsCmd, const QString &sourceId) const
596 {
597     return vcsBinary().baseName() + QLatin1Char(' ') + vcsCmd + QLatin1Char(' ')
598            + FilePath::fromString(sourceId).fileName();
599 }
600 
statusParser(const QString & text)601 void VcsBaseClient::statusParser(const QString &text)
602 {
603     QList<VcsBaseClient::StatusItem> lineInfoList;
604 
605     QStringList rawStatusList = text.split(QLatin1Char('\n'));
606 
607     foreach (const QString &string, rawStatusList) {
608         const VcsBaseClient::StatusItem lineInfo = parseStatusLine(string);
609         if (!lineInfo.flags.isEmpty() && !lineInfo.file.isEmpty())
610             lineInfoList.append(lineInfo);
611     }
612 
613     emit parsedStatus(lineInfoList);
614 }
615 
616 } // namespace VcsBase
617 
618 #include "moc_vcsbaseclient.cpp"
619