3 * This file is part of LyX, the document processor.
4 * Licence details can be found in the file COPYING.
6 * \author Lars Gullik Bjønnes
9 * Full author contact details are available in file CREDITS.
14 #include "VCBackend.h"
16 #include "DispatchResult.h"
18 #include "FuncRequest.h"
20 #include "frontends/alert.h"
21 #include "frontends/Application.h"
23 #include "support/convert.h"
24 #include "support/debug.h"
25 #include "support/filetools.h"
26 #include "support/gettext.h"
27 #include "support/lstrings.h"
28 #include "support/PathChanger.h"
29 #include "support/Systemcall.h"
30 #include "support/regex.h"
31 #include "support/TempFile.h"
36 using namespace lyx::support;
42 int VCS::doVCCommandCall(string const & cmd, FileName const & path)
44 LYXERR(Debug::LYXVC, "doVCCommandCall: " << cmd);
46 support::PathChanger p(path);
47 return one.startscript(Systemcall::Wait, cmd, string(), string(), false);
51 int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError)
54 owner_->setBusy(true);
56 int const ret = doVCCommandCall(cmd, path);
59 owner_->setBusy(false);
60 if (ret && reportError)
61 frontend::Alert::error(_("Revision control error."),
62 bformat(_("Some problem occurred while running the command:\n"
69 bool VCS::makeRCSRevision(string const &version, string &revis) const
74 int back = convert<int>(rev);
75 // if positive use as the last number in the whole revision string
78 rsplit(version, base , '.');
79 rev = base + '.' + rev;
83 // we care about the last number from revision string
84 // in case of backward indexing
87 cur = rsplit(version, base , '.');
90 int want = convert<int>(cur) + back;
94 rev = base + '.' + convert<string>(want);
103 bool VCS::checkparentdirs(FileName const & file, std::string const & vcsdir)
105 FileName dirname = file.onlyPath();
107 FileName tocheck = FileName(addName(dirname.absFileName(), vcsdir));
108 LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName());
109 if (tocheck.exists())
111 //this construct because of #8295
112 dirname = FileName(dirname.absFileName()).parentPath();
113 } while (!dirname.empty());
118 /////////////////////////////////////////////////////////////////////
122 /////////////////////////////////////////////////////////////////////
124 RCS::RCS(FileName const & m, Buffer * b) : VCS(b)
126 // Here we know that the buffer file is either already in RCS or
127 // about to be registered
133 FileName const RCS::findFile(FileName const & file)
135 // Check if *,v exists.
136 FileName tmp(file.absFileName() + ",v");
137 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
138 if (tmp.isReadableFile()) {
139 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
143 // Check if RCS/*,v exists.
144 tmp = FileName(addName(addPath(onlyPath(file.absFileName()), "RCS"), file.absFileName()) + ",v");
145 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
146 if (tmp.isReadableFile()) {
147 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
155 bool RCS::retrieve(FileName const & file)
157 LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file);
158 // The caller ensures that file does not exist, so no need to check that.
159 return doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()),
164 void RCS::scanMaster()
169 LYXERR(Debug::LYXVC, "LyXVC::RCS: scanMaster: " << master_);
171 ifstream ifs(master_.toFilesystemEncoding().c_str());
174 bool read_enough = false;
176 while (!read_enough && ifs >> token) {
177 LYXERR(Debug::LYXVC, "LyXVC::scanMaster: current lex text: `"
182 else if (token == "head") {
186 tmv = rtrim(tmv, ";");
188 LYXERR(Debug::LYXVC, "LyXVC: version found to be " << tmv);
189 } else if (contains(token, "access")
190 || contains(token, "symbols")
191 || contains(token, "strict")) {
193 } else if (contains(token, "locks")) {
195 if (contains(token, ';')) {
196 locker_ = "Unlocked";
205 s1 = rtrim(tmpt, ";");
206 // tmp is now in the format <user>:<version>
207 s1 = split(s1, s2, ':');
208 // s2 is user, and s1 is version
209 if (s1 == version_) {
214 } while (!contains(tmpt, ';'));
216 } else if (token == "comment") {
217 // we don't need to read any further than this.
221 LYXERR(Debug::LYXVC, "LyXVC::scanMaster(): unexpected token");
227 void RCS::registrer(string const & msg)
229 string cmd = "ci -q -u -i -t-\"";
232 cmd += quoteName(onlyFileName(owner_->absFileName()));
233 doVCCommand(cmd, FileName(owner_->filePath()));
237 bool RCS::renameEnabled()
243 string RCS::rename(support::FileName const & /*newFile*/, string const & /*msg*/)
245 // not implemented, since a left-over file.lyx,v would be confusing.
250 bool RCS::copyEnabled()
256 string RCS::copy(support::FileName const & newFile, string const & msg)
258 // RCS has no real copy command, so we create a poor mans version
259 support::FileName const oldFile(owner_->absFileName());
260 if (!oldFile.copyTo(newFile))
262 FileName path(oldFile.onlyPath());
263 string relFile(to_utf8(newFile.relPath(path.absFileName())));
264 string cmd = "ci -q -u -i -t-\"";
267 cmd += quoteName(relFile);
268 return doVCCommand(cmd, path) ? string() : "RCS: Proceeded";
272 LyXVC::CommandResult RCS::checkIn(string const & msg, string & log)
274 int ret = doVCCommand("ci -q -u -m\"" + msg + "\" "
275 + quoteName(onlyFileName(owner_->absFileName())),
276 FileName(owner_->filePath()));
278 return LyXVC::ErrorCommand;
279 log = "RCS: Proceeded";
280 return LyXVC::VCSuccess;
284 bool RCS::checkInEnabled()
286 return owner_ && !owner_->isReadonly();
290 bool RCS::isCheckInWithConfirmation()
292 // FIXME one day common getDiff for all backends
294 // if (getDiff(file, diff) && diff.empty())
297 TempFile tempfile("lyxvcout");
298 FileName tmpf = tempfile.name();
300 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
304 doVCCommandCall("rcsdiff " + quoteName(owner_->absFileName())
305 + " > " + quoteName(tmpf.toFilesystemEncoding()),
306 FileName(owner_->filePath()));
308 docstring diff = tmpf.fileContents("UTF-8");
317 string RCS::checkOut()
320 int ret = doVCCommand("co -q -l " + quoteName(onlyFileName(owner_->absFileName())),
321 FileName(owner_->filePath()));
322 return ret ? string() : "RCS: Proceeded";
326 bool RCS::checkOutEnabled()
328 return owner_ && owner_->isReadonly();
332 string RCS::repoUpdate()
334 lyxerr << "Sorry, not implemented." << endl;
339 bool RCS::repoUpdateEnabled()
345 string RCS::lockingToggle()
347 //FIXME this might be actually possible, study rcs -U, rcs -L.
348 //State should be easy to get inside scanMaster.
349 //It would fix #4370 and make rcs/svn usage even more closer.
350 lyxerr << "Sorry, not implemented." << endl;
355 bool RCS::lockingToggleEnabled()
363 if (doVCCommand("co -f -u" + version_ + ' '
364 + quoteName(onlyFileName(owner_->absFileName())),
365 FileName(owner_->filePath())))
367 // We ignore changes and just reload!
373 bool RCS::isRevertWithConfirmation()
375 //FIXME owner && diff ?
382 LYXERR(Debug::LYXVC, "LyXVC: undoLast");
383 doVCCommand("rcs -o" + version_ + ' '
384 + quoteName(onlyFileName(owner_->absFileName())),
385 FileName(owner_->filePath()));
389 bool RCS::undoLastEnabled()
395 void RCS::getLog(FileName const & tmpf)
397 doVCCommand("rlog " + quoteName(onlyFileName(owner_->absFileName()))
398 + " > " + quoteName(tmpf.toFilesystemEncoding()),
399 FileName(owner_->filePath()));
403 bool RCS::toggleReadOnlyEnabled()
405 // This got broken somewhere along lfuns dispatch reorganization.
406 // reloadBuffer would be needed after this, but thats problematic
407 // since we are inside Buffer::dispatch.
413 string RCS::revisionInfo(LyXVC::RevisionInfo const info)
415 if (info == LyXVC::File)
417 // fill the rest of the attributes for a single file
418 if (rev_date_cache_.empty())
419 if (!getRevisionInfo())
424 return rev_author_cache_;
426 return rev_date_cache_;
428 return rev_time_cache_;
436 bool RCS::getRevisionInfo()
438 TempFile tempfile("lyxvcout");
439 FileName tmpf = tempfile.name();
441 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
444 doVCCommand("rlog -r " + quoteName(onlyFileName(owner_->absFileName()))
445 + " > " + quoteName(tmpf.toFilesystemEncoding()),
446 FileName(owner_->filePath()));
451 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
454 // we reached to the entry, i.e. after initial log message
456 // line with critical info, e.g:
457 //"date: 2011/07/02 11:02:54; author: sanda; state: Exp; lines: +17 -2"
462 LYXERR(Debug::LYXVC, line);
463 if (entry && prefixIs(line, "date:")) {
467 if (prefixIs(line, "revision"))
473 rev_date_cache_ = token(result, ' ', 1);
474 rev_time_cache_ = rtrim(token(result, ' ', 2), ";");
475 rev_author_cache_ = trim(token(token(result, ';', 1), ':', 1));
477 return !rev_author_cache_.empty();
480 bool RCS::prepareFileRevision(string const &revis, string & f)
483 if (!VCS::makeRCSRevision(version_, rev))
486 TempFile tempfile("lyxvcrev_" + rev + '_');
487 tempfile.setAutoRemove(false);
488 FileName tmpf = tempfile.name();
490 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
494 doVCCommand("co -p" + rev + ' '
495 + quoteName(onlyFileName(owner_->absFileName()))
496 + " > " + quoteName(tmpf.toFilesystemEncoding()),
497 FileName(owner_->filePath()));
498 if (tmpf.isFileEmpty())
501 f = tmpf.absFileName();
506 bool RCS::prepareFileRevisionEnabled()
512 /////////////////////////////////////////////////////////////////////
516 /////////////////////////////////////////////////////////////////////
518 CVS::CVS(FileName const & m, Buffer * b) : VCS(b)
520 // Here we know that the buffer file is either already in CVS or
521 // about to be registered
523 have_rev_info_ = false;
528 FileName const CVS::findFile(FileName const & file)
530 // First we look for the CVS/Entries in the same dir
531 // where we have file.
532 FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
533 string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
534 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
535 << "' for `" << tmpf << '\'');
536 if (entries.isReadableFile()) {
537 // Ok we are at least in a CVS dir. Parse the CVS/Entries
538 // and see if we can find this file. We do a fast and
540 ifstream ifs(entries.toFilesystemEncoding().c_str());
542 while (getline(ifs, line)) {
543 LYXERR(Debug::LYXVC, "\tEntries: " << line);
544 if (contains(line, tmpf))
552 void CVS::scanMaster()
554 LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n Checking: " << master_);
555 // Ok now we do the real scan...
556 ifstream ifs(master_.toFilesystemEncoding().c_str());
557 string name = onlyFileName(owner_->absFileName());
558 string tmpf = '/' + name + '/';
559 LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
561 static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
562 while (getline(ifs, line)) {
563 LYXERR(Debug::LYXVC, "\t line: " << line);
564 if (contains(line, tmpf)) {
565 // Ok extract the fields.
568 regex_match(line, sm, reg);
570 //sm[0]; // whole matched string
572 version_ = sm.str(2);
573 string const file_date = sm.str(3);
576 //sm[5]; // tag or tagdate
577 FileName file(owner_->absFileName());
578 if (file.isReadableFile()) {
579 time_t mod = file.lastModified();
580 string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
581 LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
582 << "'\nModification date of file: `" << mod_date << '\'');
583 if (file.isReadOnly()) {
584 // readonly checkout is unlocked
587 FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
588 FileName base(addName(bdir.absFileName(),name));
589 // if base version is existent "cvs edit" was used to lock
590 vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
593 vcstatus = NOLOCKING;
601 bool CVS::retrieve(FileName const & file)
603 LYXERR(Debug::LYXVC, "LyXVC::CVS: retrieve.\n\t" << file);
604 // The caller ensures that file does not exist, so no need to check that.
605 return doVCCommandCall("cvs -q update " + quoteName(file.toFilesystemEncoding()),
606 file.onlyPath()) == 0;
610 string const CVS::getTarget(OperationMode opmode) const
614 // in client server mode CVS does not like full path operand for directory operation
615 // since LyX switches to the repo dir "." is good enough as target
618 return quoteName(onlyFileName(owner_->absFileName()));
624 docstring CVS::toString(CvsStatus status) const
628 return _("Up-to-date");
629 case LocallyModified:
630 return _("Locally Modified");
632 return _("Locally Added");
634 return _("Needs Merge");
636 return _("Needs Checkout");
638 return _("No CVS file");
640 return _("Cannot retrieve CVS status");
646 int CVS::doVCCommandWithOutput(string const & cmd, FileName const & path,
647 FileName const & output, bool reportError)
649 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
650 return doVCCommand(cmd + redirection, path, reportError);
654 int CVS::doVCCommandCallWithOutput(std::string const & cmd,
655 support::FileName const & path,
656 support::FileName const & output)
658 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
659 return doVCCommandCall(cmd + redirection, path);
663 CVS::CvsStatus CVS::getStatus()
665 TempFile tempfile("lyxvout");
666 FileName tmpf = tempfile.name();
668 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
672 if (doVCCommandCallWithOutput("cvs status " + getTarget(File),
673 FileName(owner_->filePath()), tmpf)) {
677 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
678 CvsStatus status = NoCvsFile;
683 LYXERR(Debug::LYXVC, line << '\n');
684 if (prefixIs(line, "File:")) {
685 if (contains(line, "Up-to-date"))
687 else if (contains(line, "Locally Modified"))
688 status = LocallyModified;
689 else if (contains(line, "Locally Added"))
690 status = LocallyAdded;
691 else if (contains(line, "Needs Merge"))
693 else if (contains(line, "Needs Checkout"))
694 status = NeedsCheckout;
700 void CVS::getRevisionInfo()
704 have_rev_info_ = true;
705 TempFile tempfile("lyxvout");
706 FileName tmpf = tempfile.name();
708 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
712 int rc = doVCCommandCallWithOutput("cvs log -r" + version_
713 + ' ' + getTarget(File),
714 FileName(owner_->filePath()), tmpf);
716 LYXERR(Debug::LYXVC, "cvs log failed with exit code " << rc);
720 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
721 static regex const reg("date: (.*) (.*) (.*); author: (.*); state: (.*);(.*)");
726 LYXERR(Debug::LYXVC, line << '\n');
727 if (prefixIs(line, "date:")) {
729 regex_match(line, sm, reg);
730 //sm[0]; // whole matched string
731 rev_date_cache_ = sm[1];
732 rev_time_cache_ = sm[2];
733 //sm[3]; // GMT offset
734 rev_author_cache_ = sm[4];
738 if (rev_author_cache_.empty())
740 "Could not retrieve revision info for " << version_ <<
741 " of " << getTarget(File));
745 void CVS::registrer(string const & msg)
747 doVCCommand("cvs -q add -m \"" + msg + "\" "
749 FileName(owner_->filePath()));
753 bool CVS::renameEnabled()
759 string CVS::rename(support::FileName const & newFile, string const & msg)
761 // CVS has no real rename command, so we create a poor mans version
762 support::FileName const oldFile(owner_->absFileName());
763 string ret = copy(newFile, msg);
766 string cmd = "cvs -q remove -m \"" + msg + "\" " +
767 quoteName(oldFile.onlyFileName());
768 FileName path(oldFile.onlyPath());
769 return doVCCommand(cmd, path) ? string() : ret;
773 bool CVS::copyEnabled()
779 string CVS::copy(support::FileName const & newFile, string const & msg)
781 // CVS has no real copy command, so we create a poor mans version
782 support::FileName const oldFile(owner_->absFileName());
783 if (!oldFile.copyTo(newFile))
785 FileName path(oldFile.onlyPath());
786 string relFile(to_utf8(newFile.relPath(path.absFileName())));
787 string cmd("cvs -q add -m \"" + msg + "\" " + quoteName(relFile));
788 return doVCCommand(cmd, path) ? string() : "CVS: Proceeded";
792 void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
794 doVCCommandWithOutput("cvs diff " + getTarget(opmode),
795 FileName(owner_->filePath()), tmpf, false);
802 return doVCCommand("cvs -q edit " + getTarget(File),
803 FileName(owner_->filePath()));
810 return doVCCommand("cvs -q unedit " + getTarget(File),
811 FileName(owner_->filePath()));
815 int CVS::update(OperationMode opmode, FileName const & tmpf)
817 return doVCCommandWithOutput("cvs -q update "
819 FileName(owner_->filePath()), tmpf, false);
823 string CVS::scanLogFile(FileName const & f, string & status)
825 ifstream ifs(f.toFilesystemEncoding().c_str());
830 LYXERR(Debug::LYXVC, line << '\n');
832 status += line + "; ";
833 if (prefixIs(line, "C ")) {
843 LyXVC::CommandResult CVS::checkIn(string const & msg, string & log)
845 CvsStatus status = getStatus();
848 if (vcstatus != NOLOCKING)
850 return LyXVC::ErrorCommand;
851 log = "CVS: Proceeded";
852 return LyXVC::VCSuccess;
853 case LocallyModified:
855 int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" "
857 FileName(owner_->filePath()));
859 return LyXVC::ErrorCommand;
860 log = "CVS: Proceeded";
861 return LyXVC::VCSuccess;
865 frontend::Alert::error(_("Revision control error."),
866 _("The repository version is newer then the current check out.\n"
867 "You have to update from repository first or revert your changes.")) ;
870 frontend::Alert::error(_("Revision control error."),
871 bformat(_("Bad status when checking in changes.\n"
876 return LyXVC::ErrorBefore;
880 bool CVS::isLocked() const
882 FileName fn(owner_->absFileName());
884 return !fn.isReadOnly();
888 bool CVS::checkInEnabled()
890 if (vcstatus != NOLOCKING)
897 bool CVS::isCheckInWithConfirmation()
899 CvsStatus status = getStatus();
900 return status == LocallyModified || status == LocallyAdded;
904 string CVS::checkOut()
906 if (vcstatus != NOLOCKING && edit())
908 TempFile tempfile("lyxvout");
909 FileName tmpf = tempfile.name();
911 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
915 int rc = update(File, tmpf);
917 string const res = scanLogFile(tmpf, log);
919 frontend::Alert::error(_("Revision control error."),
920 bformat(_("Error when updating from repository.\n"
921 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
922 "After pressing OK, LyX will try to reopen the resolved document."),
923 from_local8bit(res)));
927 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
931 bool CVS::checkOutEnabled()
933 if (vcstatus != NOLOCKING)
940 string CVS::repoUpdate()
942 TempFile tempfile("lyxvout");
943 FileName tmpf = tempfile.name();
945 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
949 getDiff(Directory, tmpf);
950 docstring res = tmpf.fileContents("UTF-8");
952 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
953 docstring const file = from_utf8(owner_->filePath());
954 docstring text = bformat(_("There were detected changes "
955 "in the working directory:\n%1$s\n\n"
956 "Possible file conflicts must be then resolved manually "
957 "or you will need to revert back to the repository version."), file);
958 int ret = frontend::Alert::prompt(_("Changes detected"),
959 text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ..."));
961 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
962 ret = frontend::Alert::prompt(_("Changes detected"),
963 text, 0, 1, _("&Continue"), _("&Abort"));
964 hideDialogs("file", 0);
970 int rc = update(Directory, tmpf);
971 res += "Update log:\n" + tmpf.fileContents("UTF-8");
972 LYXERR(Debug::LYXVC, res);
975 string sres = scanLogFile(tmpf, log);
977 docstring const file = owner_->fileName().displayName(20);
978 frontend::Alert::error(_("Revision control error."),
979 bformat(_("Error when updating document %1$s from repository.\n"
980 "You have to manually resolve the conflicts NOW!\n'%2$s'.\n\n"
981 "After pressing OK, LyX will try to reopen the resolved document."),
982 file, from_local8bit(sres)));
986 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
990 bool CVS::repoUpdateEnabled()
996 string CVS::lockingToggle()
998 lyxerr << "Sorry, not implemented." << endl;
1003 bool CVS::lockingToggleEnabled()
1009 bool CVS::isRevertWithConfirmation()
1011 CvsStatus status = getStatus();
1012 return !owner_->isClean() || status == LocallyModified || status == NeedsMerge;
1018 // Reverts to the version in CVS repository and
1019 // gets the updated version from the repository.
1020 CvsStatus status = getStatus();
1023 if (vcstatus != NOLOCKING)
1024 return 0 == unedit();
1028 case LocallyModified: {
1029 FileName f(owner_->absFileName());
1031 update(File, FileName());
1032 owner_->markClean();
1035 case LocallyAdded: {
1036 docstring const file = owner_->fileName().displayName(20);
1037 frontend::Alert::error(_("Revision control error."),
1038 bformat(_("The document %1$s is not in repository.\n"
1039 "You have to check in the first revision before you can revert."),
1044 docstring const file = owner_->fileName().displayName(20);
1045 frontend::Alert::error(_("Revision control error."),
1046 bformat(_("Cannot revert document %1$s to repository version.\n"
1047 "The status '%2$s' is unexpected."),
1048 file, toString(status)));
1056 void CVS::undoLast()
1058 // merge the current with the previous version
1059 // in a reverse patch kind of way, so that the
1060 // result is to revert the last changes.
1061 lyxerr << "Sorry, not implemented." << endl;
1065 bool CVS::undoLastEnabled()
1071 void CVS::getLog(FileName const & tmpf)
1073 doVCCommandWithOutput("cvs log " + getTarget(File),
1074 FileName(owner_->filePath()),
1079 bool CVS::toggleReadOnlyEnabled()
1085 string CVS::revisionInfo(LyXVC::RevisionInfo const info)
1087 if (!version_.empty()) {
1093 return rev_author_cache_;
1095 return rev_date_cache_;
1097 return rev_time_cache_;
1105 bool CVS::prepareFileRevision(string const & revis, string & f)
1108 if (!VCS::makeRCSRevision(version_, rev))
1111 TempFile tempfile("lyxvcrev_" + rev + '_');
1112 tempfile.setAutoRemove(false);
1113 FileName tmpf = tempfile.name();
1115 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1119 doVCCommandWithOutput("cvs update -p -r" + rev + ' '
1121 FileName(owner_->filePath()), tmpf);
1122 if (tmpf.isFileEmpty())
1125 f = tmpf.absFileName();
1130 bool CVS::prepareFileRevisionEnabled()
1136 /////////////////////////////////////////////////////////////////////
1140 /////////////////////////////////////////////////////////////////////
1142 SVN::SVN(FileName const & m, Buffer * b) : VCS(b)
1144 // Here we know that the buffer file is either already in SVN or
1145 // about to be registered
1152 FileName const SVN::findFile(FileName const & file)
1154 // First we check the existence of repository meta data.
1155 if (!VCS::checkparentdirs(file, ".svn")) {
1156 LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
1160 // Now we check the status of the file.
1161 TempFile tempfile("lyxvcout");
1162 FileName tmpf = tempfile.name();
1164 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1168 string const fname = onlyFileName(file.absFileName());
1169 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
1170 bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
1171 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1173 LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
1174 return found ? file : FileName();
1178 void SVN::scanMaster()
1180 // vcstatus code is somewhat superflous,
1181 // until we want to implement read-only toggle for svn.
1182 vcstatus = NOLOCKING;
1183 if (checkLockMode()) {
1187 vcstatus = UNLOCKED;
1192 bool SVN::checkLockMode()
1194 TempFile tempfile("lyxvcout");
1195 FileName tmpf = tempfile.name();
1197 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1201 LYXERR(Debug::LYXVC, "Detecting locking mode...");
1202 if (doVCCommandCall("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1203 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1204 FileName(owner_->filePath())))
1207 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1211 while (ifs && !ret) {
1213 LYXERR(Debug::LYXVC, line);
1214 if (contains(line, "svn:needs-lock"))
1217 LYXERR(Debug::LYXVC, "Locking enabled: " << ret);
1225 bool SVN::isLocked() const
1227 FileName file(owner_->absFileName());
1229 return !file.isReadOnly();
1233 bool SVN::retrieve(FileName const & file)
1235 LYXERR(Debug::LYXVC, "LyXVC::SVN: retrieve.\n\t" << file);
1236 // The caller ensures that file does not exist, so no need to check that.
1237 return doVCCommandCall("svn update -q --non-interactive " + quoteName(file.onlyFileName()),
1238 file.onlyPath()) == 0;
1242 void SVN::registrer(string const & /*msg*/)
1244 doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
1245 FileName(owner_->filePath()));
1249 bool SVN::renameEnabled()
1255 string SVN::rename(support::FileName const & newFile, string const & msg)
1257 // svn move does not require a log message, since it does not commit.
1258 // In LyX we commit immediately afterwards, otherwise it could be
1259 // confusing to the user to have two uncommitted files.
1260 FileName path(owner_->filePath());
1261 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1262 string cmd("svn move -q " + quoteName(onlyFileName(owner_->absFileName())) +
1263 ' ' + quoteName(relFile));
1264 if (doVCCommand(cmd, path)) {
1265 cmd = "svn revert -q " +
1266 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1268 doVCCommand(cmd, path);
1269 if (newFile.exists())
1270 newFile.removeFile();
1273 vector<support::FileName> f;
1274 f.push_back(owner_->fileName());
1275 f.push_back(newFile);
1277 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1278 cmd = "svn revert -q " +
1279 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1281 doVCCommand(cmd, path);
1282 if (newFile.exists())
1283 newFile.removeFile();
1290 bool SVN::copyEnabled()
1296 string SVN::copy(support::FileName const & newFile, string const & msg)
1298 // svn copy does not require a log message, since it does not commit.
1299 // In LyX we commit immediately afterwards, otherwise it could be
1300 // confusing to the user to have an uncommitted file.
1301 FileName path(owner_->filePath());
1302 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1303 string cmd("svn copy -q " + quoteName(onlyFileName(owner_->absFileName())) +
1304 ' ' + quoteName(relFile));
1305 if (doVCCommand(cmd, path))
1307 vector<support::FileName> f(1, newFile);
1309 if (checkIn(f, msg, log) == LyXVC::VCSuccess)
1315 LyXVC::CommandResult SVN::checkIn(string const & msg, string & log)
1317 vector<support::FileName> f(1, owner_->fileName());
1318 return checkIn(f, msg, log);
1322 LyXVC::CommandResult
1323 SVN::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1325 TempFile tempfile("lyxvcout");
1326 FileName tmpf = tempfile.name();
1328 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1329 log = N_("Error: Could not generate logfile.");
1330 return LyXVC::ErrorBefore;
1334 os << "svn commit -m \"" << msg << '"';
1335 for (size_t i = 0; i < f.size(); ++i)
1336 os << ' ' << quoteName(f[i].onlyFileName());
1337 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1338 LyXVC::CommandResult ret =
1339 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1340 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1342 string res = scanLogFile(tmpf, log);
1344 frontend::Alert::error(_("Revision control error."),
1345 _("Error when committing to repository.\n"
1346 "You have to manually resolve the problem.\n"
1347 "LyX will reopen the document after you press OK."));
1348 ret = LyXVC::ErrorCommand;
1351 if (!fileLock(false, tmpf, log))
1352 ret = LyXVC::ErrorCommand;
1355 log.insert(0, "SVN: ");
1356 if (ret == LyXVC::VCSuccess && log.empty())
1357 log = "SVN: Proceeded";
1362 bool SVN::checkInEnabled()
1371 bool SVN::isCheckInWithConfirmation()
1373 // FIXME one day common getDiff and perhaps OpMode for all backends
1375 TempFile tempfile("lyxvcout");
1376 FileName tmpf = tempfile.name();
1378 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1382 doVCCommandCall("svn diff " + quoteName(owner_->absFileName())
1383 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1384 FileName(owner_->filePath()));
1386 docstring diff = tmpf.fileContents("UTF-8");
1395 // FIXME Correctly return code should be checked instead of this.
1396 // This would need another solution than just plain startscript.
1397 // Hint from Andre': QProcess::readAllStandardError()...
1398 string SVN::scanLogFile(FileName const & f, string & status)
1400 ifstream ifs(f.toFilesystemEncoding().c_str());
1405 LYXERR(Debug::LYXVC, line << '\n');
1407 status += line + "; ";
1408 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
1409 || contains(line, "Commit failed")) {
1413 if (contains(line, "svn:needs-lock")) {
1423 bool SVN::fileLock(bool lock, FileName const & tmpf, string &status)
1425 if (!locked_mode_ || (isLocked() == lock))
1428 string const arg = lock ? "lock " : "unlock ";
1429 doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName()))
1430 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1431 FileName(owner_->filePath()));
1433 // Lock error messages go unfortunately on stderr and are unreachible this way.
1434 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1438 if (!line.empty()) status += line + "; ";
1442 if (isLocked() == lock)
1446 frontend::Alert::error(_("Revision control error."),
1447 _("Error while acquiring write lock.\n"
1448 "Another user is most probably editing\n"
1449 "the current document now!\n"
1450 "Also check the access to the repository."));
1452 frontend::Alert::error(_("Revision control error."),
1453 _("Error while releasing write lock.\n"
1454 "Check the access to the repository."));
1459 string SVN::checkOut()
1461 TempFile tempfile("lyxvcout");
1462 FileName tmpf = tempfile.name();
1464 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1465 return N_("Error: Could not generate logfile.");
1468 doVCCommand("svn update --non-interactive " + quoteName(onlyFileName(owner_->absFileName()))
1469 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1470 FileName(owner_->filePath()));
1473 string const res = scanLogFile(tmpf, log);
1475 frontend::Alert::error(_("Revision control error."),
1476 bformat(_("Error when updating from repository.\n"
1477 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
1478 "After pressing OK, LyX will try to reopen the resolved document."),
1479 from_local8bit(res)));
1481 fileLock(true, tmpf, log);
1483 return log.empty() ? string() : "SVN: " + log;
1487 bool SVN::checkOutEnabled()
1496 string SVN::repoUpdate()
1498 TempFile tempfile("lyxvcout");
1499 FileName tmpf = tempfile.name();
1501 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1502 return N_("Error: Could not generate logfile.");
1505 doVCCommand("svn diff " + quoteName(owner_->filePath())
1506 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1507 FileName(owner_->filePath()));
1508 docstring res = tmpf.fileContents("UTF-8");
1510 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
1511 docstring const file = from_utf8(owner_->filePath());
1512 docstring text = bformat(_("There were detected changes "
1513 "in the working directory:\n%1$s\n\n"
1514 "In case of file conflict version of the local directory files "
1515 "will be preferred."
1516 "\n\nContinue?"), file);
1517 int ret = frontend::Alert::prompt(_("Changes detected"),
1518 text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
1520 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
1521 ret = frontend::Alert::prompt(_("Changes detected"),
1522 text, 0, 1, _("&Yes"), _("&No"));
1523 hideDialogs("file", 0);
1529 // Reverting looks too harsh, see bug #6255.
1530 // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
1531 // + " > " + quoteName(tmpf.toFilesystemEncoding()),
1532 // FileName(owner_->filePath()));
1533 // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
1534 doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
1535 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1536 FileName(owner_->filePath()));
1537 res += "Update log:\n" + tmpf.fileContents("UTF-8");
1539 LYXERR(Debug::LYXVC, res);
1540 return to_utf8(res);
1544 bool SVN::repoUpdateEnabled()
1550 string SVN::lockingToggle()
1552 TempFile tempfile("lyxvcout");
1553 FileName tmpf = tempfile.name();
1555 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1556 return N_("Error: Could not generate logfile.");
1559 int ret = doVCCommand("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1560 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1561 FileName(owner_->filePath()));
1566 string res = scanLogFile(tmpf, log);
1567 bool locking = contains(res, "svn:needs-lock");
1569 ret = doVCCommand("svn propset svn:needs-lock ON "
1570 + quoteName(onlyFileName(owner_->absFileName()))
1571 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1572 FileName(owner_->filePath()));
1574 ret = doVCCommand("svn propdel svn:needs-lock "
1575 + quoteName(onlyFileName(owner_->absFileName()))
1576 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1577 FileName(owner_->filePath()));
1581 frontend::Alert::warning(_("SVN File Locking"),
1582 (locking ? _("Locking property unset.") : _("Locking property set.")) + '\n'
1583 + _("Do not forget to commit the locking property into the repository."),
1586 return string("SVN: ") + (locking ?
1587 N_("Locking property unset.") : N_("Locking property set."));
1591 bool SVN::lockingToggleEnabled()
1599 // Reverts to the version in SVN repository and
1600 // gets the updated version from the repository.
1601 string const fil = quoteName(onlyFileName(owner_->absFileName()));
1603 if (doVCCommand("svn revert -q " + fil,
1604 FileName(owner_->filePath())))
1606 owner_->markClean();
1611 bool SVN::isRevertWithConfirmation()
1613 //FIXME owner && diff
1618 void SVN::undoLast()
1620 // merge the current with the previous version
1621 // in a reverse patch kind of way, so that the
1622 // result is to revert the last changes.
1623 lyxerr << "Sorry, not implemented." << endl;
1627 bool SVN::undoLastEnabled()
1633 string SVN::revisionInfo(LyXVC::RevisionInfo const info)
1635 if (info == LyXVC::Tree) {
1636 if (rev_tree_cache_.empty())
1637 if (!getTreeRevisionInfo())
1638 rev_tree_cache_ = "?";
1639 if (rev_tree_cache_ == "?")
1642 return rev_tree_cache_;
1645 // fill the rest of the attributes for a single file
1646 if (rev_file_cache_.empty())
1647 if (!getFileRevisionInfo())
1648 rev_file_cache_ = "?";
1652 if (rev_file_cache_ == "?")
1654 return rev_file_cache_;
1656 return rev_author_cache_;
1658 return rev_date_cache_;
1660 return rev_time_cache_;
1669 bool SVN::getFileRevisionInfo()
1671 TempFile tempfile("lyxvcout");
1672 FileName tmpf = tempfile.name();
1674 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1678 doVCCommand("svn info --xml " + quoteName(onlyFileName(owner_->absFileName()))
1679 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1680 FileName(owner_->filePath()));
1685 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1693 LYXERR(Debug::LYXVC, line);
1694 if (prefixIs(line, "<commit"))
1696 if (c && prefixIs(line, " revision=\"") && suffixIs(line, "\">")) {
1697 string l1 = subst(line, "revision=\"", "");
1698 string l2 = trim(subst(l1, "\">", ""));
1700 rev_file_cache_ = rev = l2;
1702 if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
1703 string l1 = subst(line, "<author>", "");
1704 string l2 = subst(l1, "</author>", "");
1705 rev_author_cache_ = l2;
1707 if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
1708 string l1 = subst(line, "<date>", "");
1709 string l2 = subst(l1, "</date>", "");
1710 l2 = split(l2, l1, 'T');
1711 rev_date_cache_ = l1;
1712 l2 = split(l2, l1, '.');
1713 rev_time_cache_ = l1;
1718 return !rev.empty();
1722 bool SVN::getTreeRevisionInfo()
1724 TempFile tempfile("lyxvcout");
1725 FileName tmpf = tempfile.name();
1727 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1731 doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
1732 FileName(owner_->filePath()));
1737 // only first line in case something bad happens.
1738 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1743 rev_tree_cache_ = line;
1744 return !line.empty();
1748 void SVN::getLog(FileName const & tmpf)
1750 doVCCommand("svn log " + quoteName(onlyFileName(owner_->absFileName()))
1751 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1752 FileName(owner_->filePath()));
1756 bool SVN::prepareFileRevision(string const & revis, string & f)
1758 if (!isStrInt(revis))
1761 int rev = convert<int>(revis);
1763 if (!getFileRevisionInfo())
1766 rev = convert<int>(rev_file_cache_);
1767 // go back for minus rev
1769 rev = rev + convert<int>(rev_file_cache_);
1774 string revname = convert<string>(rev);
1775 TempFile tempfile("lyxvcrev_" + revname + '_');
1776 tempfile.setAutoRemove(false);
1777 FileName tmpf = tempfile.name();
1779 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1783 doVCCommand("svn cat -r " + revname + ' '
1784 + quoteName(onlyFileName(owner_->absFileName()))
1785 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1786 FileName(owner_->filePath()));
1787 if (tmpf.isFileEmpty())
1790 f = tmpf.absFileName();
1795 bool SVN::prepareFileRevisionEnabled()
1802 bool SVN::toggleReadOnlyEnabled()
1808 /////////////////////////////////////////////////////////////////////
1812 /////////////////////////////////////////////////////////////////////
1814 GIT::GIT(FileName const & m, Buffer * b) : VCS(b)
1816 // Here we know that the buffer file is either already in GIT or
1817 // about to be registered
1823 FileName const GIT::findFile(FileName const & file)
1825 // First we check the existence of repository meta data.
1826 if (!VCS::checkparentdirs(file, ".git")) {
1827 LYXERR(Debug::LYXVC, "Cannot find GIT meta data for " << file);
1831 // Now we check the status of the file.
1832 TempFile tempfile("lyxvcout");
1833 FileName tmpf = tempfile.name();
1835 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1839 string const fname = onlyFileName(file.absFileName());
1840 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under git control for `"
1842 doVCCommandCall("git ls-files " +
1843 quoteName(fname) + " > " +
1844 quoteName(tmpf.toFilesystemEncoding()),
1846 bool found = !tmpf.isFileEmpty();
1847 LYXERR(Debug::LYXVC, "GIT control: " << (found ? "enabled" : "disabled"));
1848 return found ? file : FileName();
1852 void GIT::scanMaster()
1854 // vcstatus code is somewhat superflous,
1855 // until we want to implement read-only toggle for git.
1856 vcstatus = NOLOCKING;
1860 bool GIT::retrieve(FileName const & file)
1862 LYXERR(Debug::LYXVC, "LyXVC::GIT: retrieve.\n\t" << file);
1863 // The caller ensures that file does not exist, so no need to check that.
1864 return doVCCommandCall("git checkout -q " + quoteName(file.onlyFileName()),
1865 file.onlyPath()) == 0;
1869 void GIT::registrer(string const & /*msg*/)
1871 doVCCommand("git add " + quoteName(onlyFileName(owner_->absFileName())),
1872 FileName(owner_->filePath()));
1876 bool GIT::renameEnabled()
1882 string GIT::rename(support::FileName const & newFile, string const & msg)
1884 // git mv does not require a log message, since it does not commit.
1885 // In LyX we commit immediately afterwards, otherwise it could be
1886 // confusing to the user to have two uncommitted files.
1887 FileName path(owner_->filePath());
1888 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1889 string cmd("git mv " + quoteName(onlyFileName(owner_->absFileName())) +
1890 ' ' + quoteName(relFile));
1891 if (doVCCommand(cmd, path)) {
1892 cmd = "git checkout -q " +
1893 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1895 doVCCommand(cmd, path);
1896 if (newFile.exists())
1897 newFile.removeFile();
1900 vector<support::FileName> f;
1901 f.push_back(owner_->fileName());
1902 f.push_back(newFile);
1904 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1905 cmd = "git checkout -q " +
1906 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1908 doVCCommand(cmd, path);
1909 if (newFile.exists())
1910 newFile.removeFile();
1917 bool GIT::copyEnabled()
1923 string GIT::copy(support::FileName const & /*newFile*/, string const & /*msg*/)
1925 // git does not support copy with history preservation
1930 LyXVC::CommandResult GIT::checkIn(string const & msg, string & log)
1932 vector<support::FileName> f(1, owner_->fileName());
1933 return checkIn(f, msg, log);
1937 LyXVC::CommandResult
1938 GIT::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1940 TempFile tempfile("lyxvcout");
1941 FileName tmpf = tempfile.name();
1943 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1944 log = N_("Error: Could not generate logfile.");
1945 return LyXVC::ErrorBefore;
1949 os << "git commit -m \"" << msg << '"';
1950 for (size_t i = 0; i < f.size(); ++i)
1951 os << ' ' << quoteName(f[i].onlyFileName());
1952 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1953 LyXVC::CommandResult ret =
1954 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1955 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1957 string res = scanLogFile(tmpf, log);
1959 frontend::Alert::error(_("Revision control error."),
1960 _("Error when committing to repository.\n"
1961 "You have to manually resolve the problem.\n"
1962 "LyX will reopen the document after you press OK."));
1963 ret = LyXVC::ErrorCommand;
1967 log.insert(0, "GIT: ");
1968 if (ret == LyXVC::VCSuccess && log.empty())
1969 log = "GIT: Proceeded";
1974 bool GIT::checkInEnabled()
1980 bool GIT::isCheckInWithConfirmation()
1982 // FIXME one day common getDiff and perhaps OpMode for all backends
1984 TempFile tempfile("lyxvcout");
1985 FileName tmpf = tempfile.name();
1987 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1991 doVCCommandCall("git diff " + quoteName(owner_->absFileName())
1992 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1993 FileName(owner_->filePath()));
1995 docstring diff = tmpf.fileContents("UTF-8");
2004 // FIXME Correctly return code should be checked instead of this.
2005 // This would need another solution than just plain startscript.
2006 // Hint from Andre': QProcess::readAllStandardError()...
2007 string GIT::scanLogFile(FileName const & f, string & status)
2009 ifstream ifs(f.toFilesystemEncoding().c_str());
2014 LYXERR(Debug::LYXVC, line << "\n");
2016 status += line + "; ";
2017 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
2018 || contains(line, "Commit failed")) {
2028 string GIT::checkOut()
2034 bool GIT::checkOutEnabled()
2040 string GIT::repoUpdate()
2046 bool GIT::repoUpdateEnabled()
2052 string GIT::lockingToggle()
2058 bool GIT::lockingToggleEnabled()
2066 // Reverts to the version in GIT repository and
2067 // gets the updated version from the repository.
2068 string const fil = quoteName(onlyFileName(owner_->absFileName()));
2070 if (doVCCommand("git checkout -q " + fil,
2071 FileName(owner_->filePath())))
2073 owner_->markClean();
2078 bool GIT::isRevertWithConfirmation()
2080 //FIXME owner && diff
2085 void GIT::undoLast()
2087 // merge the current with the previous version
2088 // in a reverse patch kind of way, so that the
2089 // result is to revert the last changes.
2090 lyxerr << "Sorry, not implemented." << endl;
2094 bool GIT::undoLastEnabled()
2100 string GIT::revisionInfo(LyXVC::RevisionInfo const info)
2102 if (info == LyXVC::Tree) {
2103 if (rev_tree_cache_.empty())
2104 if (!getTreeRevisionInfo())
2105 rev_tree_cache_ = "?";
2106 if (rev_tree_cache_ == "?")
2109 return rev_tree_cache_;
2112 // fill the rest of the attributes for a single file
2113 if (rev_file_cache_.empty())
2114 if (!getFileRevisionInfo())
2115 rev_file_cache_ = "?";
2119 if (rev_file_cache_ == "?")
2121 return rev_file_cache_;
2123 return rev_author_cache_;
2125 return rev_date_cache_;
2127 return rev_time_cache_;
2136 bool GIT::getFileRevisionInfo()
2138 TempFile tempfile("lyxvcout");
2139 FileName tmpf = tempfile.name();
2141 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2145 doVCCommand("git log -n 1 --pretty=format:%H%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName()))
2146 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2147 FileName(owner_->filePath()));
2152 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2155 getline(ifs, rev_file_cache_);
2157 getline(ifs, rev_author_cache_);
2161 rev_time_cache_ = split(line, rev_date_cache_, ' ');
2165 return !rev_file_cache_.empty();
2169 bool GIT::getTreeRevisionInfo()
2171 TempFile tempfile("lyxvcout");
2172 FileName tmpf = tempfile.name();
2174 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2178 doVCCommand("git describe --abbrev --dirty --long > " + quoteName(tmpf.toFilesystemEncoding()),
2179 FileName(owner_->filePath()));
2184 // only first line in case something bad happens.
2185 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2186 getline(ifs, rev_tree_cache_);
2189 return !rev_tree_cache_.empty();
2193 void GIT::getLog(FileName const & tmpf)
2195 doVCCommand("git log " + quoteName(onlyFileName(owner_->absFileName()))
2196 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2197 FileName(owner_->filePath()));
2201 //at this moment we don't accept revision SHA, but just number of revision steps back
2202 //GUI and infrastucture needs to be changed first
2203 bool GIT::prepareFileRevision(string const & revis, string & f)
2205 // anything positive means we got hash, not "0" or minus revision
2208 // hash is rarely number and should be long
2209 if (isStrInt(revis) && revis.length()<20)
2210 rev = convert<int>(revis);
2212 // revision and filename
2215 // go back for "minus" revisions
2217 pointer = "HEAD~" + convert<string>(-rev);
2224 TempFile tempfile("lyxvcrev_" + revis + '_');
2225 tempfile.setAutoRemove(false);
2226 FileName tmpf = tempfile.name();
2228 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2232 doVCCommand("git show " + pointer + "./"
2233 + quoteName(onlyFileName(owner_->absFileName()))
2234 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2235 FileName(owner_->filePath()));
2236 if (tmpf.isFileEmpty())
2239 f = tmpf.absFileName();
2244 bool GIT::prepareFileRevisionEnabled()
2250 bool GIT::toggleReadOnlyEnabled()