]> git.lyx.org Git - features.git/blob - src/LyXVC.cpp
Comment
[features.git] / src / LyXVC.cpp
1 /**
2  * \file LyXVC.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Lars Gullik Bjønnes
7  * \author Jean-Marc Lasgouttes
8  * \author Angus Leeming
9  * \author John Levon
10  * \author André Pönitz
11  * \author Allan Rae
12  *
13  * Full author contact details are available in file CREDITS.
14  */
15
16 #include <config.h>
17
18 #include "LyXVC.h"
19 #include "VCBackend.h"
20 #include "Buffer.h"
21
22 #include "frontends/alert.h"
23
24 #include "support/debug.h"
25 #include "support/filetools.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
28 #include "support/TempFile.h"
29
30 using namespace std;
31 using namespace lyx::support;
32
33 namespace lyx {
34
35 namespace Alert = frontend::Alert;
36
37
38 LyXVC::LyXVC()
39 {
40         owner_ = nullptr;
41 }
42
43
44 docstring LyXVC::vcstatus() const
45 {
46         if (!vcs_)
47                 return docstring();
48         if (locking())
49                 return bformat(_("%1$s lock"), from_ascii(vcs_->vcname()));
50         else
51                 return from_ascii(vcs_->vcname());
52 }
53
54
55 bool LyXVC::fileInVC(FileName const & fn)
56 {
57         if (!RCS::findFile(fn).empty())
58                 return true;
59         if (!CVS::findFile(fn).empty())
60                 return true;
61         if (!SVN::findFile(fn).empty())
62                 return true;
63         if (!GIT::findFile(fn).empty())
64                 return true;
65         return false;
66 }
67
68
69 bool LyXVC::file_found_hook(FileName const & fn)
70 {
71         FileName found_file;
72         // Check if file is under RCS
73         if (!(found_file = RCS::findFile(fn)).empty()) {
74                 vcs_.reset(new RCS(found_file, owner_));
75                 return true;
76         }
77         // Check if file is under CVS
78         if (!(found_file = CVS::findFile(fn)).empty()) {
79                 vcs_.reset(new CVS(found_file, owner_));
80                 return true;
81         }
82         // Check if file is under SVN
83         if (!(found_file = SVN::findFile(fn)).empty()) {
84                 vcs_.reset(new SVN(found_file, owner_));
85                 return true;
86         }
87         // Check if file is under GIT
88         if (!(found_file = GIT::findFile(fn)).empty()) {
89                 vcs_.reset(new GIT(found_file, owner_));
90                 return true;
91         }
92
93         // file is not under any VCS.
94         vcs_.reset(nullptr);
95         return false;
96 }
97
98
99 bool LyXVC::file_not_found_hook(FileName const & fn)
100 {
101         // Check if file is under version control.
102         // This happens if we are trying to load does not exist.
103         // It may yet exist in the repository and so could be
104         // checked out.
105         bool foundRCS = !RCS::findFile(fn).empty();
106         bool foundCVS = foundRCS ? false : !CVS::findFile(fn).empty();
107         bool foundSVN = (foundRCS || foundCVS) ? false : !SVN::findFile(fn).empty();
108         bool foundGIT = (foundRCS || foundCVS || foundSVN) ? false : !GIT::findFile(fn).empty();
109         if (foundRCS || foundCVS || foundSVN || foundGIT) {
110                 docstring const file = makeDisplayPath(fn.absFileName(), 20);
111                 docstring const text =
112                         bformat(_("Do you want to retrieve the document"
113                                                    " %1$s from version control?"), file);
114                 int const ret = Alert::prompt(_("Retrieve from version control?"),
115                         text, 0, 1, _("&Retrieve"), _("&Cancel"));
116
117                 if (ret == 0) {
118                         // Since the retrieve commands are implemented using
119                         // more general update commands we need to ensure that
120                         // we do not change an existing file by accident.
121                         if (fn.exists())
122                                 return false;
123                         if (foundRCS)
124                                 return RCS::retrieve(fn);
125                         else if (foundCVS)
126                                 return CVS::retrieve(fn);
127                         else if (foundSVN)
128                                 return SVN::retrieve(fn);
129                         else
130                                 return GIT::retrieve(fn);
131                 }
132         }
133         return false;
134 }
135
136
137 void LyXVC::setBuffer(Buffer * buf)
138 {
139         owner_ = buf;
140 }
141
142
143 bool LyXVC::registrer()
144 {
145         FileName const filename = owner_->fileName();
146
147         // there must be a file to save
148         if (!filename.isReadableFile()) {
149                 Alert::error(_("Document not saved"),
150                              _("You must save the document "
151                                             "before it can be registered."));
152                 return false;
153         }
154
155         // it is very likely here that the vcs is not created yet...
156         if (!vcs_) {
157                 FileName found = VCS::checkParentDirs(filename, ".git/index");
158
159                 if (!found.empty()) {
160                         LYXERR(Debug::LYXVC, "LyXVC: registering "
161                                 << to_utf8(filename.displayName()) << " with GIT");
162                         vcs_.reset(new GIT(found, owner_));
163
164                 } else {
165                         found = VCS::checkParentDirs(filename, ".svn/entries");
166                         if (!found.empty()) {
167                                 LYXERR(Debug::LYXVC, "LyXVC: registering "
168                                         << to_utf8(filename.displayName()) << " with SVN");
169                                 vcs_.reset(new SVN(found, owner_));
170
171                         } else {
172                                 // We only need to check the current directory, since CVS meta-data
173                                 // is in every sub-directory.
174                                 FileName const cvs_entries(onlyPath(filename.absFileName()) + "/CVS/Entries");
175                                 if (cvs_entries.isReadableFile()) {
176                                         LYXERR(Debug::LYXVC, "LyXVC: registering "
177                                                 << to_utf8(filename.displayName()) << " with CVS");
178                                         vcs_.reset(new CVS(cvs_entries, owner_));
179
180                                 } else {
181                                         // If all else fails, use RCS
182                                         LYXERR(Debug::LYXVC, "LyXVC: registering "
183                                                 << to_utf8(filename.displayName()) << " with RCS");
184                                         vcs_.reset(new RCS(FileName(), owner_));
185                                 }
186                         }
187                 }
188         }
189
190         LYXERR(Debug::LYXVC, "LyXVC: registrer");
191         docstring response;
192         bool ok = Alert::askForText(response, _("LyX VC: Initial description"),
193                         _("(no initial description)"));
194         if (!ok) {
195                 LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
196                 vcs_.reset(nullptr);
197                 return false;
198         }
199         if (response.empty())
200                 response = _("(no initial description)");
201         // FIXME This will fail with svn if the current directory has not
202         // itself been added.
203         vcs_->registrer(to_utf8(response));
204         return true;
205 }
206
207
208 string LyXVC::rename(FileName const & fn)
209 {
210         LYXERR(Debug::LYXVC, "LyXVC: rename");
211         if (!vcs_ || fileInVC(fn))
212                 return string();
213         docstring response;
214         bool ok = Alert::askForText(response, _("LyX VC: Log message"),
215                         _("(no log message)"));
216         if (!ok) {
217                 LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
218                 return string();
219         }
220         if (response.empty())
221                 response = _("(no log message)");
222         string ret = vcs_->rename(fn, to_utf8(response));
223         return ret;
224 }
225
226
227 string LyXVC::copy(FileName const & fn)
228 {
229         LYXERR(Debug::LYXVC, "LyXVC: copy");
230         if (!vcs_ || fileInVC(fn))
231                 return string();
232         docstring response;
233         bool ok = Alert::askForText(response, _("LyX VC: Log message"),
234                         _("(no log message)"));
235         if (!ok) {
236                 LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
237                 return string();
238         }
239         if (response.empty())
240                 response = _("(no log message)");
241         string ret = vcs_->copy(fn, to_utf8(response));
242         return ret;
243 }
244
245
246 LyXVC::CommandResult LyXVC::checkIn(string & log)
247 {
248         LYXERR(Debug::LYXVC, "LyXVC: checkIn");
249         if (!vcs_)
250                 return ErrorBefore;
251         docstring empty(_("(no log message)"));
252         docstring response;
253         bool ok = true;
254         if (vcs_->isCheckInWithConfirmation())
255                 ok = Alert::askForText(response, _("LyX VC: Log Message"));
256         if (ok) {
257                 if (response.empty())
258                         response = empty;
259                 //shell collisions
260                 response = subst(response, from_ascii("\""), from_ascii("\\\""));
261                 return vcs_->checkIn(to_utf8(response), log);
262         } else {
263                 LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
264                 return Cancelled;
265         }
266 }
267
268
269 string LyXVC::checkOut()
270 {
271         if (!vcs_)
272                 return string();
273         //RCS allows checkOut only in ReadOnly mode
274         if (vcs_->toggleReadOnlyEnabled() && !owner_->hasReadonlyFlag())
275                 return string();
276
277         LYXERR(Debug::LYXVC, "LyXVC: checkOut");
278         return vcs_->checkOut();
279 }
280
281
282 string LyXVC::repoUpdate()
283 {
284         LYXERR(Debug::LYXVC, "LyXVC: repoUpdate");
285         if (!vcs_)
286                 return string();
287         return vcs_->repoUpdate();
288 }
289
290
291 string LyXVC::lockingToggle()
292 {
293         LYXERR(Debug::LYXVC, "LyXVC: toggle locking property");
294         if (!vcs_)
295                 return string();
296         return vcs_->lockingToggle();
297 }
298
299
300 bool LyXVC::revert()
301 {
302         LYXERR(Debug::LYXVC, "LyXVC: revert");
303         if (!vcs_)
304                 return false;
305
306         docstring const file = owner_->fileName().displayName(20);
307         docstring text = bformat(_("Reverting to the stored version of the "
308                                 "document %1$s will lose all current changes.\n\n"
309                                 "Do you want to revert to the older version?"), file);
310         int ret = 0;
311         if (vcs_->isRevertWithConfirmation())
312                 ret = Alert::prompt(_("Revert to stored version of document?"),
313                         text, 0, 1, _("&Revert"), _("&Cancel"));
314
315         return ret == 0 && vcs_->revert();
316 }
317
318
319 void LyXVC::undoLast()
320 {
321         if (!vcs_)
322                 return;
323         vcs_->undoLast();
324 }
325
326
327 string LyXVC::toggleReadOnly()
328 {
329         if (!vcs_)
330                 return string();
331         if (!vcs_->toggleReadOnlyEnabled())
332                 return string();
333
334         switch (vcs_->status()) {
335         case VCS::UNLOCKED:
336                 LYXERR(Debug::LYXVC, "LyXVC: toggle to locked");
337                 return checkOut();
338         case VCS::LOCKED: {
339                 LYXERR(Debug::LYXVC, "LyXVC: toggle to unlocked");
340                 string log;
341                 if (checkIn(log) != VCSuccess)
342                         return string();
343                 return log;
344         }
345         case VCS::NOLOCKING:
346                 Buffer * b = vcs_->owner();
347                 bool const newstate = !b->hasReadonlyFlag();
348                 string result = "LyXVC: toggle to ";
349                 result += (newstate ? "readonly" : "readwrite");
350                 LYXERR(Debug::LYXVC, result);
351                 b->setReadonly(newstate);
352                 return result;
353         }
354         return string();
355 }
356
357
358 bool LyXVC::inUse() const
359 {
360         return vcs_ != nullptr;
361 }
362
363
364 string const LyXVC::versionString() const
365 {
366         if (!vcs_)
367                 return string();
368         return vcs_->versionString();
369 }
370
371
372 bool LyXVC::locking() const
373 {
374         if (!vcs_)
375                 return false;
376         return vcs_->status() != VCS::NOLOCKING;
377 }
378
379
380 string const LyXVC::getLogFile() const
381 {
382         if (!vcs_)
383                 return string();
384
385         TempFile tempfile("lyxvclog");
386         tempfile.setAutoRemove(false);
387         FileName const tmpf = tempfile.name();
388         if (tmpf.empty()) {
389                 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
390                 return string();
391         }
392         LYXERR(Debug::LYXVC, "Generating logfile " << tmpf);
393         vcs_->getLog(tmpf);
394         return tmpf.absFileName();
395 }
396
397
398 string LyXVC::revisionInfo(RevisionInfo const info) const
399 {
400         if (!vcs_)
401                 return string();
402
403         return vcs_->revisionInfo(info);
404 }
405
406
407 bool LyXVC::renameEnabled() const
408 {
409         if (!inUse())
410                 return false;
411         return vcs_->renameEnabled();
412 }
413
414
415 bool LyXVC::copyEnabled() const
416 {
417         if (!inUse())
418                 return false;
419         return vcs_->copyEnabled();
420 }
421
422
423 bool LyXVC::checkOutEnabled() const
424 {
425         return vcs_ && vcs_->checkOutEnabled();
426 }
427
428
429 bool LyXVC::checkInEnabled() const
430 {
431         return vcs_ && vcs_->checkInEnabled();
432 }
433
434
435 bool LyXVC::isCheckInWithConfirmation() const
436 {
437         return vcs_ && vcs_->isCheckInWithConfirmation();
438 }
439
440
441 bool LyXVC::lockingToggleEnabled() const
442 {
443         return vcs_ && vcs_->lockingToggleEnabled();
444 }
445
446
447 bool LyXVC::undoLastEnabled() const
448 {
449         return vcs_ && vcs_->undoLastEnabled();
450 }
451
452
453 bool LyXVC::repoUpdateEnabled() const
454 {
455         return vcs_ && vcs_->repoUpdateEnabled();
456 }
457
458
459 bool LyXVC::prepareFileRevision(string const & rev, std::string & f)
460 {
461         return vcs_ && vcs_->prepareFileRevision(rev, f);
462 }
463
464
465 bool LyXVC::prepareFileRevisionEnabled()
466 {
467         return vcs_ && vcs_->prepareFileRevisionEnabled();
468 }
469
470 } // namespace lyx