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()));
499 if (tmpf.isFileEmpty())
502 f = tmpf.absFileName();
507 bool RCS::prepareFileRevisionEnabled()
513 /////////////////////////////////////////////////////////////////////
517 /////////////////////////////////////////////////////////////////////
519 CVS::CVS(FileName const & m, Buffer * b) : VCS(b)
521 // Here we know that the buffer file is either already in CVS or
522 // about to be registered
524 have_rev_info_ = false;
529 FileName const CVS::findFile(FileName const & file)
531 // First we look for the CVS/Entries in the same dir
532 // where we have file.
533 FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
534 string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
535 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
536 << "' for `" << tmpf << '\'');
537 if (entries.isReadableFile()) {
538 // Ok we are at least in a CVS dir. Parse the CVS/Entries
539 // and see if we can find this file. We do a fast and
541 ifstream ifs(entries.toFilesystemEncoding().c_str());
543 while (getline(ifs, line)) {
544 LYXERR(Debug::LYXVC, "\tEntries: " << line);
545 if (contains(line, tmpf))
553 void CVS::scanMaster()
555 LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n Checking: " << master_);
556 // Ok now we do the real scan...
557 ifstream ifs(master_.toFilesystemEncoding().c_str());
558 string name = onlyFileName(owner_->absFileName());
559 string tmpf = '/' + name + '/';
560 LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
562 static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
563 while (getline(ifs, line)) {
564 LYXERR(Debug::LYXVC, "\t line: " << line);
565 if (contains(line, tmpf)) {
566 // Ok extract the fields.
569 regex_match(line, sm, reg);
571 //sm[0]; // whole matched string
573 version_ = sm.str(2);
574 string const file_date = sm.str(3);
577 //sm[5]; // tag or tagdate
578 FileName file(owner_->absFileName());
579 if (file.isReadableFile()) {
580 time_t mod = file.lastModified();
581 string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
582 LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
583 << "'\nModification date of file: `" << mod_date << '\'');
584 if (file.isReadOnly()) {
585 // readonly checkout is unlocked
588 FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
589 FileName base(addName(bdir.absFileName(),name));
590 // if base version is existent "cvs edit" was used to lock
591 vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
594 vcstatus = NOLOCKING;
602 bool CVS::retrieve(FileName const & file)
604 LYXERR(Debug::LYXVC, "LyXVC::CVS: retrieve.\n\t" << file);
605 // The caller ensures that file does not exist, so no need to check that.
606 return doVCCommandCall("cvs -q update " + quoteName(file.toFilesystemEncoding()),
607 file.onlyPath()) == 0;
611 string const CVS::getTarget(OperationMode opmode) const
615 // in client server mode CVS does not like full path operand for directory operation
616 // since LyX switches to the repo dir "." is good enough as target
619 return quoteName(onlyFileName(owner_->absFileName()));
625 docstring CVS::toString(CvsStatus status) const
629 return _("Up-to-date");
630 case LocallyModified:
631 return _("Locally Modified");
633 return _("Locally Added");
635 return _("Needs Merge");
637 return _("Needs Checkout");
639 return _("No CVS file");
641 return _("Cannot retrieve CVS status");
647 int CVS::doVCCommandWithOutput(string const & cmd, FileName const & path,
648 FileName const & output, bool reportError)
650 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
651 return doVCCommand(cmd + redirection, path, reportError);
655 int CVS::doVCCommandCallWithOutput(std::string const & cmd,
656 support::FileName const & path,
657 support::FileName const & output)
659 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
660 return doVCCommandCall(cmd + redirection, path);
664 CVS::CvsStatus CVS::getStatus()
666 TempFile tempfile("lyxvout");
667 FileName tmpf = tempfile.name();
669 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
673 if (doVCCommandCallWithOutput("cvs status " + getTarget(File),
674 FileName(owner_->filePath()), tmpf)) {
678 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
679 CvsStatus status = NoCvsFile;
684 LYXERR(Debug::LYXVC, line << '\n');
685 if (prefixIs(line, "File:")) {
686 if (contains(line, "Up-to-date"))
688 else if (contains(line, "Locally Modified"))
689 status = LocallyModified;
690 else if (contains(line, "Locally Added"))
691 status = LocallyAdded;
692 else if (contains(line, "Needs Merge"))
694 else if (contains(line, "Needs Checkout"))
695 status = NeedsCheckout;
701 void CVS::getRevisionInfo()
705 have_rev_info_ = true;
706 TempFile tempfile("lyxvout");
707 FileName tmpf = tempfile.name();
709 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
713 int rc = doVCCommandCallWithOutput("cvs log -r" + version_
714 + ' ' + getTarget(File),
715 FileName(owner_->filePath()), tmpf);
717 LYXERR(Debug::LYXVC, "cvs log failed with exit code " << rc);
721 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
722 static regex const reg("date: (.*) (.*) (.*); author: (.*); state: (.*);(.*)");
727 LYXERR(Debug::LYXVC, line << '\n');
728 if (prefixIs(line, "date:")) {
730 regex_match(line, sm, reg);
731 //sm[0]; // whole matched string
732 rev_date_cache_ = sm[1];
733 rev_time_cache_ = sm[2];
734 //sm[3]; // GMT offset
735 rev_author_cache_ = sm[4];
739 if (rev_author_cache_.empty())
741 "Could not retrieve revision info for " << version_ <<
742 " of " << getTarget(File));
746 void CVS::registrer(string const & msg)
748 doVCCommand("cvs -q add -m \"" + msg + "\" "
750 FileName(owner_->filePath()));
754 bool CVS::renameEnabled()
760 string CVS::rename(support::FileName const & newFile, string const & msg)
762 // CVS has no real rename command, so we create a poor mans version
763 support::FileName const oldFile(owner_->absFileName());
764 string ret = copy(newFile, msg);
767 string cmd = "cvs -q remove -m \"" + msg + "\" " +
768 quoteName(oldFile.onlyFileName());
769 FileName path(oldFile.onlyPath());
770 return doVCCommand(cmd, path) ? string() : ret;
774 bool CVS::copyEnabled()
780 string CVS::copy(support::FileName const & newFile, string const & msg)
782 // CVS has no real copy command, so we create a poor mans version
783 support::FileName const oldFile(owner_->absFileName());
784 if (!oldFile.copyTo(newFile))
786 FileName path(oldFile.onlyPath());
787 string relFile(to_utf8(newFile.relPath(path.absFileName())));
788 string cmd("cvs -q add -m \"" + msg + "\" " + quoteName(relFile));
789 return doVCCommand(cmd, path) ? string() : "CVS: Proceeded";
793 void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
795 doVCCommandWithOutput("cvs diff " + getTarget(opmode),
796 FileName(owner_->filePath()), tmpf, false);
803 return doVCCommand("cvs -q edit " + getTarget(File),
804 FileName(owner_->filePath()));
811 return doVCCommand("cvs -q unedit " + getTarget(File),
812 FileName(owner_->filePath()));
816 int CVS::update(OperationMode opmode, FileName const & tmpf)
818 return doVCCommandWithOutput("cvs -q update "
820 FileName(owner_->filePath()), tmpf, false);
824 string CVS::scanLogFile(FileName const & f, string & status)
826 ifstream ifs(f.toFilesystemEncoding().c_str());
831 LYXERR(Debug::LYXVC, line << '\n');
833 status += line + "; ";
834 if (prefixIs(line, "C ")) {
844 LyXVC::CommandResult CVS::checkIn(string const & msg, string & log)
846 CvsStatus status = getStatus();
849 if (vcstatus != NOLOCKING)
851 return LyXVC::ErrorCommand;
852 log = "CVS: Proceeded";
853 return LyXVC::VCSuccess;
854 case LocallyModified:
856 int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" "
858 FileName(owner_->filePath()));
860 return LyXVC::ErrorCommand;
861 log = "CVS: Proceeded";
862 return LyXVC::VCSuccess;
866 frontend::Alert::error(_("Revision control error."),
867 _("The repository version is newer then the current check out.\n"
868 "You have to update from repository first or revert your changes.")) ;
871 frontend::Alert::error(_("Revision control error."),
872 bformat(_("Bad status when checking in changes.\n"
877 return LyXVC::ErrorBefore;
881 bool CVS::isLocked() const
883 FileName fn(owner_->absFileName());
885 return !fn.isReadOnly();
889 bool CVS::checkInEnabled()
891 if (vcstatus != NOLOCKING)
898 bool CVS::isCheckInWithConfirmation()
900 CvsStatus status = getStatus();
901 return status == LocallyModified || status == LocallyAdded;
905 string CVS::checkOut()
907 if (vcstatus != NOLOCKING && edit())
909 TempFile tempfile("lyxvout");
910 FileName tmpf = tempfile.name();
912 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
916 int rc = update(File, tmpf);
918 string const res = scanLogFile(tmpf, log);
920 frontend::Alert::error(_("Revision control error."),
921 bformat(_("Error when updating from repository.\n"
922 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
923 "After pressing OK, LyX will try to reopen the resolved document."),
924 from_local8bit(res)));
928 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
932 bool CVS::checkOutEnabled()
934 if (vcstatus != NOLOCKING)
941 string CVS::repoUpdate()
943 TempFile tempfile("lyxvout");
944 FileName tmpf = tempfile.name();
946 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
950 getDiff(Directory, tmpf);
951 docstring res = tmpf.fileContents("UTF-8");
953 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
954 docstring const file = from_utf8(owner_->filePath());
955 docstring text = bformat(_("There were detected changes "
956 "in the working directory:\n%1$s\n\n"
957 "Possible file conflicts must be then resolved manually "
958 "or you will need to revert back to the repository version."), file);
959 int ret = frontend::Alert::prompt(_("Changes detected"),
960 text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ..."));
962 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
963 ret = frontend::Alert::prompt(_("Changes detected"),
964 text, 0, 1, _("&Continue"), _("&Abort"));
965 hideDialogs("file", 0);
971 int rc = update(Directory, tmpf);
972 res += "Update log:\n" + tmpf.fileContents("UTF-8");
973 LYXERR(Debug::LYXVC, res);
976 string sres = scanLogFile(tmpf, log);
978 docstring const file = owner_->fileName().displayName(20);
979 frontend::Alert::error(_("Revision control error."),
980 bformat(_("Error when updating document %1$s from repository.\n"
981 "You have to manually resolve the conflicts NOW!\n'%2$s'.\n\n"
982 "After pressing OK, LyX will try to reopen the resolved document."),
983 file, from_local8bit(sres)));
987 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
991 bool CVS::repoUpdateEnabled()
997 string CVS::lockingToggle()
999 lyxerr << "Sorry, not implemented." << endl;
1004 bool CVS::lockingToggleEnabled()
1010 bool CVS::isRevertWithConfirmation()
1012 CvsStatus status = getStatus();
1013 return !owner_->isClean() || status == LocallyModified || status == NeedsMerge;
1019 // Reverts to the version in CVS repository and
1020 // gets the updated version from the repository.
1021 CvsStatus status = getStatus();
1024 if (vcstatus != NOLOCKING)
1025 return 0 == unedit();
1029 case LocallyModified: {
1030 FileName f(owner_->absFileName());
1032 update(File, FileName());
1033 owner_->markClean();
1036 case LocallyAdded: {
1037 docstring const file = owner_->fileName().displayName(20);
1038 frontend::Alert::error(_("Revision control error."),
1039 bformat(_("The document %1$s is not in repository.\n"
1040 "You have to check in the first revision before you can revert."),
1045 docstring const file = owner_->fileName().displayName(20);
1046 frontend::Alert::error(_("Revision control error."),
1047 bformat(_("Cannot revert document %1$s to repository version.\n"
1048 "The status '%2$s' is unexpected."),
1049 file, toString(status)));
1057 void CVS::undoLast()
1059 // merge the current with the previous version
1060 // in a reverse patch kind of way, so that the
1061 // result is to revert the last changes.
1062 lyxerr << "Sorry, not implemented." << endl;
1066 bool CVS::undoLastEnabled()
1072 void CVS::getLog(FileName const & tmpf)
1074 doVCCommandWithOutput("cvs log " + getTarget(File),
1075 FileName(owner_->filePath()),
1080 bool CVS::toggleReadOnlyEnabled()
1086 string CVS::revisionInfo(LyXVC::RevisionInfo const info)
1088 if (!version_.empty()) {
1094 return rev_author_cache_;
1096 return rev_date_cache_;
1098 return rev_time_cache_;
1106 bool CVS::prepareFileRevision(string const & revis, string & f)
1109 if (!VCS::makeRCSRevision(version_, rev))
1112 TempFile tempfile("lyxvcrev_" + rev + '_');
1113 tempfile.setAutoRemove(false);
1114 FileName tmpf = tempfile.name();
1116 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1120 doVCCommandWithOutput("cvs update -p -r" + rev + ' '
1122 FileName(owner_->filePath()), tmpf);
1124 if (tmpf.isFileEmpty())
1127 f = tmpf.absFileName();
1132 bool CVS::prepareFileRevisionEnabled()
1138 /////////////////////////////////////////////////////////////////////
1142 /////////////////////////////////////////////////////////////////////
1144 SVN::SVN(FileName const & m, Buffer * b) : VCS(b)
1146 // Here we know that the buffer file is either already in SVN or
1147 // about to be registered
1154 FileName const SVN::findFile(FileName const & file)
1156 // First we check the existence of repository meta data.
1157 if (!VCS::checkparentdirs(file, ".svn")) {
1158 LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
1162 // Now we check the status of the file.
1163 TempFile tempfile("lyxvcout");
1164 FileName tmpf = tempfile.name();
1166 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1170 string const fname = onlyFileName(file.absFileName());
1171 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
1172 bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
1173 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1175 LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
1176 return found ? file : FileName();
1180 void SVN::scanMaster()
1182 // vcstatus code is somewhat superflous,
1183 // until we want to implement read-only toggle for svn.
1184 vcstatus = NOLOCKING;
1185 if (checkLockMode()) {
1189 vcstatus = UNLOCKED;
1194 bool SVN::checkLockMode()
1196 TempFile tempfile("lyxvcout");
1197 FileName tmpf = tempfile.name();
1199 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1203 LYXERR(Debug::LYXVC, "Detecting locking mode...");
1204 if (doVCCommandCall("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1205 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1206 FileName(owner_->filePath())))
1209 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1213 while (ifs && !ret) {
1215 LYXERR(Debug::LYXVC, line);
1216 if (contains(line, "svn:needs-lock"))
1219 LYXERR(Debug::LYXVC, "Locking enabled: " << ret);
1227 bool SVN::isLocked() const
1229 FileName file(owner_->absFileName());
1231 return !file.isReadOnly();
1235 bool SVN::retrieve(FileName const & file)
1237 LYXERR(Debug::LYXVC, "LyXVC::SVN: retrieve.\n\t" << file);
1238 // The caller ensures that file does not exist, so no need to check that.
1239 return doVCCommandCall("svn update -q --non-interactive " + quoteName(file.onlyFileName()),
1240 file.onlyPath()) == 0;
1244 void SVN::registrer(string const & /*msg*/)
1246 doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
1247 FileName(owner_->filePath()));
1251 bool SVN::renameEnabled()
1257 string SVN::rename(support::FileName const & newFile, string const & msg)
1259 // svn move does not require a log message, since it does not commit.
1260 // In LyX we commit immediately afterwards, otherwise it could be
1261 // confusing to the user to have two uncommitted files.
1262 FileName path(owner_->filePath());
1263 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1264 string cmd("svn move -q " + quoteName(onlyFileName(owner_->absFileName())) +
1265 ' ' + quoteName(relFile));
1266 if (doVCCommand(cmd, path)) {
1267 cmd = "svn revert -q " +
1268 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1270 doVCCommand(cmd, path);
1271 if (newFile.exists())
1272 newFile.removeFile();
1275 vector<support::FileName> f;
1276 f.push_back(owner_->fileName());
1277 f.push_back(newFile);
1279 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1280 cmd = "svn revert -q " +
1281 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1283 doVCCommand(cmd, path);
1284 if (newFile.exists())
1285 newFile.removeFile();
1292 bool SVN::copyEnabled()
1298 string SVN::copy(support::FileName const & newFile, string const & msg)
1300 // svn copy does not require a log message, since it does not commit.
1301 // In LyX we commit immediately afterwards, otherwise it could be
1302 // confusing to the user to have an uncommitted file.
1303 FileName path(owner_->filePath());
1304 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1305 string cmd("svn copy -q " + quoteName(onlyFileName(owner_->absFileName())) +
1306 ' ' + quoteName(relFile));
1307 if (doVCCommand(cmd, path))
1309 vector<support::FileName> f(1, newFile);
1311 if (checkIn(f, msg, log) == LyXVC::VCSuccess)
1317 LyXVC::CommandResult SVN::checkIn(string const & msg, string & log)
1319 vector<support::FileName> f(1, owner_->fileName());
1320 return checkIn(f, msg, log);
1324 LyXVC::CommandResult
1325 SVN::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1327 TempFile tempfile("lyxvcout");
1328 FileName tmpf = tempfile.name();
1330 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1331 log = N_("Error: Could not generate logfile.");
1332 return LyXVC::ErrorBefore;
1336 os << "svn commit -m \"" << msg << '"';
1337 for (size_t i = 0; i < f.size(); ++i)
1338 os << ' ' << quoteName(f[i].onlyFileName());
1339 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1340 LyXVC::CommandResult ret =
1341 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1342 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1344 string res = scanLogFile(tmpf, log);
1346 frontend::Alert::error(_("Revision control error."),
1347 _("Error when committing to repository.\n"
1348 "You have to manually resolve the problem.\n"
1349 "LyX will reopen the document after you press OK."));
1350 ret = LyXVC::ErrorCommand;
1353 if (!fileLock(false, tmpf, log))
1354 ret = LyXVC::ErrorCommand;
1357 log.insert(0, "SVN: ");
1358 if (ret == LyXVC::VCSuccess && log.empty())
1359 log = "SVN: Proceeded";
1364 bool SVN::checkInEnabled()
1373 bool SVN::isCheckInWithConfirmation()
1375 // FIXME one day common getDiff and perhaps OpMode for all backends
1377 TempFile tempfile("lyxvcout");
1378 FileName tmpf = tempfile.name();
1380 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1384 doVCCommandCall("svn diff " + quoteName(owner_->absFileName())
1385 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1386 FileName(owner_->filePath()));
1388 docstring diff = tmpf.fileContents("UTF-8");
1397 // FIXME Correctly return code should be checked instead of this.
1398 // This would need another solution than just plain startscript.
1399 // Hint from Andre': QProcess::readAllStandardError()...
1400 string SVN::scanLogFile(FileName const & f, string & status)
1402 ifstream ifs(f.toFilesystemEncoding().c_str());
1407 LYXERR(Debug::LYXVC, line << '\n');
1409 status += line + "; ";
1410 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
1411 || contains(line, "Commit failed")) {
1415 if (contains(line, "svn:needs-lock")) {
1425 bool SVN::fileLock(bool lock, FileName const & tmpf, string &status)
1427 if (!locked_mode_ || (isLocked() == lock))
1430 string const arg = lock ? "lock " : "unlock ";
1431 doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName()))
1432 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1433 FileName(owner_->filePath()));
1435 // Lock error messages go unfortunately on stderr and are unreachible this way.
1436 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1440 if (!line.empty()) status += line + "; ";
1444 if (isLocked() == lock)
1448 frontend::Alert::error(_("Revision control error."),
1449 _("Error while acquiring write lock.\n"
1450 "Another user is most probably editing\n"
1451 "the current document now!\n"
1452 "Also check the access to the repository."));
1454 frontend::Alert::error(_("Revision control error."),
1455 _("Error while releasing write lock.\n"
1456 "Check the access to the repository."));
1461 string SVN::checkOut()
1463 TempFile tempfile("lyxvcout");
1464 FileName tmpf = tempfile.name();
1466 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1467 return N_("Error: Could not generate logfile.");
1470 doVCCommand("svn update --non-interactive " + quoteName(onlyFileName(owner_->absFileName()))
1471 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1472 FileName(owner_->filePath()));
1475 string const res = scanLogFile(tmpf, log);
1477 frontend::Alert::error(_("Revision control error."),
1478 bformat(_("Error when updating from repository.\n"
1479 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
1480 "After pressing OK, LyX will try to reopen the resolved document."),
1481 from_local8bit(res)));
1483 fileLock(true, tmpf, log);
1485 return log.empty() ? string() : "SVN: " + log;
1489 bool SVN::checkOutEnabled()
1498 string SVN::repoUpdate()
1500 TempFile tempfile("lyxvcout");
1501 FileName tmpf = tempfile.name();
1503 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1504 return N_("Error: Could not generate logfile.");
1507 doVCCommand("svn diff " + quoteName(owner_->filePath())
1508 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1509 FileName(owner_->filePath()));
1510 docstring res = tmpf.fileContents("UTF-8");
1512 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
1513 docstring const file = from_utf8(owner_->filePath());
1514 docstring text = bformat(_("There were detected changes "
1515 "in the working directory:\n%1$s\n\n"
1516 "In case of file conflict version of the local directory files "
1517 "will be preferred."
1518 "\n\nContinue?"), file);
1519 int ret = frontend::Alert::prompt(_("Changes detected"),
1520 text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
1522 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
1523 ret = frontend::Alert::prompt(_("Changes detected"),
1524 text, 0, 1, _("&Yes"), _("&No"));
1525 hideDialogs("file", 0);
1531 // Reverting looks too harsh, see bug #6255.
1532 // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
1533 // + " > " + quoteName(tmpf.toFilesystemEncoding()),
1534 // FileName(owner_->filePath()));
1535 // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
1536 doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
1537 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1538 FileName(owner_->filePath()));
1539 res += "Update log:\n" + tmpf.fileContents("UTF-8");
1541 LYXERR(Debug::LYXVC, res);
1542 return to_utf8(res);
1546 bool SVN::repoUpdateEnabled()
1552 string SVN::lockingToggle()
1554 TempFile tempfile("lyxvcout");
1555 FileName tmpf = tempfile.name();
1557 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1558 return N_("Error: Could not generate logfile.");
1561 int ret = doVCCommand("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1562 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1563 FileName(owner_->filePath()));
1568 string res = scanLogFile(tmpf, log);
1569 bool locking = contains(res, "svn:needs-lock");
1571 ret = doVCCommand("svn propset svn:needs-lock ON "
1572 + quoteName(onlyFileName(owner_->absFileName()))
1573 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1574 FileName(owner_->filePath()));
1576 ret = doVCCommand("svn propdel svn:needs-lock "
1577 + quoteName(onlyFileName(owner_->absFileName()))
1578 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1579 FileName(owner_->filePath()));
1583 frontend::Alert::warning(_("SVN File Locking"),
1584 (locking ? _("Locking property unset.") : _("Locking property set.")) + '\n'
1585 + _("Do not forget to commit the locking property into the repository."),
1588 return string("SVN: ") + (locking ?
1589 N_("Locking property unset.") : N_("Locking property set."));
1593 bool SVN::lockingToggleEnabled()
1601 // Reverts to the version in SVN repository and
1602 // gets the updated version from the repository.
1603 string const fil = quoteName(onlyFileName(owner_->absFileName()));
1605 if (doVCCommand("svn revert -q " + fil,
1606 FileName(owner_->filePath())))
1608 owner_->markClean();
1613 bool SVN::isRevertWithConfirmation()
1615 //FIXME owner && diff
1620 void SVN::undoLast()
1622 // merge the current with the previous version
1623 // in a reverse patch kind of way, so that the
1624 // result is to revert the last changes.
1625 lyxerr << "Sorry, not implemented." << endl;
1629 bool SVN::undoLastEnabled()
1635 string SVN::revisionInfo(LyXVC::RevisionInfo const info)
1637 if (info == LyXVC::Tree) {
1638 if (rev_tree_cache_.empty())
1639 if (!getTreeRevisionInfo())
1640 rev_tree_cache_ = "?";
1641 if (rev_tree_cache_ == "?")
1644 return rev_tree_cache_;
1647 // fill the rest of the attributes for a single file
1648 if (rev_file_cache_.empty())
1649 if (!getFileRevisionInfo())
1650 rev_file_cache_ = "?";
1654 if (rev_file_cache_ == "?")
1656 return rev_file_cache_;
1658 return rev_author_cache_;
1660 return rev_date_cache_;
1662 return rev_time_cache_;
1671 bool SVN::getFileRevisionInfo()
1673 TempFile tempfile("lyxvcout");
1674 FileName tmpf = tempfile.name();
1676 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1680 doVCCommand("svn info --xml " + quoteName(onlyFileName(owner_->absFileName()))
1681 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1682 FileName(owner_->filePath()));
1687 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1695 LYXERR(Debug::LYXVC, line);
1696 if (prefixIs(line, "<commit"))
1698 if (c && prefixIs(line, " revision=\"") && suffixIs(line, "\">")) {
1699 string l1 = subst(line, "revision=\"", "");
1700 string l2 = trim(subst(l1, "\">", ""));
1702 rev_file_cache_ = rev = l2;
1704 if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
1705 string l1 = subst(line, "<author>", "");
1706 string l2 = subst(l1, "</author>", "");
1707 rev_author_cache_ = l2;
1709 if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
1710 string l1 = subst(line, "<date>", "");
1711 string l2 = subst(l1, "</date>", "");
1712 l2 = split(l2, l1, 'T');
1713 rev_date_cache_ = l1;
1714 l2 = split(l2, l1, '.');
1715 rev_time_cache_ = l1;
1720 return !rev.empty();
1724 bool SVN::getTreeRevisionInfo()
1726 TempFile tempfile("lyxvcout");
1727 FileName tmpf = tempfile.name();
1729 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1733 doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
1734 FileName(owner_->filePath()));
1739 // only first line in case something bad happens.
1740 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1745 rev_tree_cache_ = line;
1746 return !line.empty();
1750 void SVN::getLog(FileName const & tmpf)
1752 doVCCommand("svn log " + quoteName(onlyFileName(owner_->absFileName()))
1753 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1754 FileName(owner_->filePath()));
1758 bool SVN::prepareFileRevision(string const & revis, string & f)
1760 if (!isStrInt(revis))
1763 int rev = convert<int>(revis);
1765 if (!getFileRevisionInfo())
1768 rev = convert<int>(rev_file_cache_);
1769 // go back for minus rev
1771 rev = rev + convert<int>(rev_file_cache_);
1776 string revname = convert<string>(rev);
1777 TempFile tempfile("lyxvcrev_" + revname + '_');
1778 tempfile.setAutoRemove(false);
1779 FileName tmpf = tempfile.name();
1781 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1785 doVCCommand("svn cat -r " + revname + ' '
1786 + quoteName(onlyFileName(owner_->absFileName()))
1787 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1788 FileName(owner_->filePath()));
1790 if (tmpf.isFileEmpty())
1793 f = tmpf.absFileName();
1798 bool SVN::prepareFileRevisionEnabled()
1805 bool SVN::toggleReadOnlyEnabled()
1811 /////////////////////////////////////////////////////////////////////
1815 /////////////////////////////////////////////////////////////////////
1817 GIT::GIT(FileName const & m, Buffer * b) : VCS(b)
1819 // Here we know that the buffer file is either already in GIT or
1820 // about to be registered
1826 FileName const GIT::findFile(FileName const & file)
1828 // First we check the existence of repository meta data.
1829 if (!VCS::checkparentdirs(file, ".git")) {
1830 LYXERR(Debug::LYXVC, "Cannot find GIT meta data for " << file);
1834 // Now we check the status of the file.
1835 TempFile tempfile("lyxvcout");
1836 FileName tmpf = tempfile.name();
1838 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1842 string const fname = onlyFileName(file.absFileName());
1843 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under git control for `"
1845 doVCCommandCall("git ls-files " +
1846 quoteName(fname) + " > " +
1847 quoteName(tmpf.toFilesystemEncoding()),
1850 bool found = !tmpf.isFileEmpty();
1851 LYXERR(Debug::LYXVC, "GIT control: " << (found ? "enabled" : "disabled"));
1852 return found ? file : FileName();
1856 void GIT::scanMaster()
1858 // vcstatus code is somewhat superflous,
1859 // until we want to implement read-only toggle for git.
1860 vcstatus = NOLOCKING;
1864 bool GIT::retrieve(FileName const & file)
1866 LYXERR(Debug::LYXVC, "LyXVC::GIT: retrieve.\n\t" << file);
1867 // The caller ensures that file does not exist, so no need to check that.
1868 return doVCCommandCall("git checkout -q " + quoteName(file.onlyFileName()),
1869 file.onlyPath()) == 0;
1873 void GIT::registrer(string const & /*msg*/)
1875 doVCCommand("git add " + quoteName(onlyFileName(owner_->absFileName())),
1876 FileName(owner_->filePath()));
1880 bool GIT::renameEnabled()
1886 string GIT::rename(support::FileName const & newFile, string const & msg)
1888 // git mv does not require a log message, since it does not commit.
1889 // In LyX we commit immediately afterwards, otherwise it could be
1890 // confusing to the user to have two uncommitted files.
1891 FileName path(owner_->filePath());
1892 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1893 string cmd("git mv " + quoteName(onlyFileName(owner_->absFileName())) +
1894 ' ' + quoteName(relFile));
1895 if (doVCCommand(cmd, path)) {
1896 cmd = "git checkout -q " +
1897 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1899 doVCCommand(cmd, path);
1900 if (newFile.exists())
1901 newFile.removeFile();
1904 vector<support::FileName> f;
1905 f.push_back(owner_->fileName());
1906 f.push_back(newFile);
1908 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1909 cmd = "git checkout -q " +
1910 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1912 doVCCommand(cmd, path);
1913 if (newFile.exists())
1914 newFile.removeFile();
1921 bool GIT::copyEnabled()
1927 string GIT::copy(support::FileName const & /*newFile*/, string const & /*msg*/)
1929 // git does not support copy with history preservation
1934 LyXVC::CommandResult GIT::checkIn(string const & msg, string & log)
1936 vector<support::FileName> f(1, owner_->fileName());
1937 return checkIn(f, msg, log);
1941 LyXVC::CommandResult
1942 GIT::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1944 TempFile tempfile("lyxvcout");
1945 FileName tmpf = tempfile.name();
1947 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1948 log = N_("Error: Could not generate logfile.");
1949 return LyXVC::ErrorBefore;
1953 os << "git commit -m \"" << msg << '"';
1954 for (size_t i = 0; i < f.size(); ++i)
1955 os << ' ' << quoteName(f[i].onlyFileName());
1956 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1957 LyXVC::CommandResult ret =
1958 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1959 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1961 string res = scanLogFile(tmpf, log);
1963 frontend::Alert::error(_("Revision control error."),
1964 _("Error when committing to repository.\n"
1965 "You have to manually resolve the problem.\n"
1966 "LyX will reopen the document after you press OK."));
1967 ret = LyXVC::ErrorCommand;
1971 log.insert(0, "GIT: ");
1972 if (ret == LyXVC::VCSuccess && log.empty())
1973 log = "GIT: Proceeded";
1978 bool GIT::checkInEnabled()
1984 bool GIT::isCheckInWithConfirmation()
1986 // FIXME one day common getDiff and perhaps OpMode for all backends
1988 TempFile tempfile("lyxvcout");
1989 FileName tmpf = tempfile.name();
1991 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1995 doVCCommandCall("git diff " + quoteName(owner_->absFileName())
1996 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1997 FileName(owner_->filePath()));
1999 docstring diff = tmpf.fileContents("UTF-8");
2008 // FIXME Correctly return code should be checked instead of this.
2009 // This would need another solution than just plain startscript.
2010 // Hint from Andre': QProcess::readAllStandardError()...
2011 string GIT::scanLogFile(FileName const & f, string & status)
2013 ifstream ifs(f.toFilesystemEncoding().c_str());
2018 LYXERR(Debug::LYXVC, line << "\n");
2020 status += line + "; ";
2021 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
2022 || contains(line, "Commit failed")) {
2032 string GIT::checkOut()
2038 bool GIT::checkOutEnabled()
2044 string GIT::repoUpdate()
2050 bool GIT::repoUpdateEnabled()
2056 string GIT::lockingToggle()
2062 bool GIT::lockingToggleEnabled()
2070 // Reverts to the version in GIT repository and
2071 // gets the updated version from the repository.
2072 string const fil = quoteName(onlyFileName(owner_->absFileName()));
2074 if (doVCCommand("git checkout -q " + fil,
2075 FileName(owner_->filePath())))
2077 owner_->markClean();
2082 bool GIT::isRevertWithConfirmation()
2084 //FIXME owner && diff
2089 void GIT::undoLast()
2091 // merge the current with the previous version
2092 // in a reverse patch kind of way, so that the
2093 // result is to revert the last changes.
2094 lyxerr << "Sorry, not implemented." << endl;
2098 bool GIT::undoLastEnabled()
2104 string GIT::revisionInfo(LyXVC::RevisionInfo const info)
2106 if (info == LyXVC::Tree) {
2107 if (rev_tree_cache_.empty())
2108 if (!getTreeRevisionInfo())
2109 rev_tree_cache_ = "?";
2110 if (rev_tree_cache_ == "?")
2113 return rev_tree_cache_;
2116 // fill the rest of the attributes for a single file
2117 if (rev_file_cache_.empty())
2118 if (!getFileRevisionInfo())
2119 rev_file_cache_ = "?";
2123 if (rev_file_cache_ == "?")
2125 return rev_file_cache_;
2127 return rev_author_cache_;
2129 return rev_date_cache_;
2131 return rev_time_cache_;
2140 bool GIT::getFileRevisionInfo()
2142 TempFile tempfile("lyxvcout");
2143 FileName tmpf = tempfile.name();
2145 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2149 doVCCommand("git log -n 1 --pretty=format:%H%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName()))
2150 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2151 FileName(owner_->filePath()));
2156 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2159 getline(ifs, rev_file_cache_);
2161 getline(ifs, rev_author_cache_);
2165 rev_time_cache_ = split(line, rev_date_cache_, ' ');
2169 return !rev_file_cache_.empty();
2173 bool GIT::getTreeRevisionInfo()
2175 TempFile tempfile("lyxvcout");
2176 FileName tmpf = tempfile.name();
2178 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2182 doVCCommand("git describe --abbrev --dirty --long > " + quoteName(tmpf.toFilesystemEncoding()),
2183 FileName(owner_->filePath()));
2188 // only first line in case something bad happens.
2189 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2190 getline(ifs, rev_tree_cache_);
2193 return !rev_tree_cache_.empty();
2197 void GIT::getLog(FileName const & tmpf)
2199 doVCCommand("git log " + quoteName(onlyFileName(owner_->absFileName()))
2200 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2201 FileName(owner_->filePath()));
2205 //at this moment we don't accept revision SHA, but just number of revision steps back
2206 //GUI and infrastucture needs to be changed first
2207 bool GIT::prepareFileRevision(string const & revis, string & f)
2209 // anything positive means we got hash, not "0" or minus revision
2212 // hash is rarely number and should be long
2213 if (isStrInt(revis) && revis.length()<20)
2214 rev = convert<int>(revis);
2216 // revision and filename
2219 // go back for "minus" revisions
2221 pointer = "HEAD~" + convert<string>(-rev);
2228 TempFile tempfile("lyxvcrev_" + revis + '_');
2229 tempfile.setAutoRemove(false);
2230 FileName tmpf = tempfile.name();
2232 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2236 doVCCommand("git show " + pointer + "./"
2237 + quoteName(onlyFileName(owner_->absFileName()))
2238 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2239 FileName(owner_->filePath()));
2241 if (tmpf.isFileEmpty())
2244 f = tmpf.absFileName();
2249 bool GIT::prepareFileRevisionEnabled()
2255 bool GIT::toggleReadOnlyEnabled()