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