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"
38 using namespace lyx::support;
44 int VCS::doVCCommandCall(string const & cmd, FileName const & path)
46 LYXERR(Debug::LYXVC, "doVCCommandCall: " << cmd);
48 support::PathChanger p(path);
49 return one.startscript(Systemcall::Wait, cmd, string(), string(), false);
53 int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError)
56 owner_->setBusy(true);
58 int const ret = doVCCommandCall(cmd, path);
61 owner_->setBusy(false);
62 if (ret && reportError)
63 frontend::Alert::error(_("Revision control error."),
64 bformat(_("Some problem occurred while running the command:\n"
71 bool VCS::makeRCSRevision(string const &version, string &revis) const
76 int back = convert<int>(rev);
77 // if positive use as the last number in the whole revision string
80 rsplit(version, base , '.');
81 rev = base + '.' + rev;
85 // we care about the last number from revision string
86 // in case of backward indexing
89 cur = rsplit(version, base , '.');
92 int want = convert<int>(cur) + back;
96 rev = base + '.' + convert<string>(want);
105 bool VCS::checkparentdirs(FileName const & file, std::string const & vcsdir)
107 FileName dirname = file.onlyPath();
109 FileName tocheck = FileName(addName(dirname.absFileName(), vcsdir));
110 LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName());
111 if (tocheck.exists())
113 //this construct because of #8295
114 dirname = FileName(dirname.absFileName()).parentPath();
115 } while (!dirname.empty());
120 /////////////////////////////////////////////////////////////////////
124 /////////////////////////////////////////////////////////////////////
126 RCS::RCS(FileName const & m, Buffer * b) : VCS(b)
128 // Here we know that the buffer file is either already in RCS or
129 // about to be registered
135 FileName const RCS::findFile(FileName const & file)
137 // Check if *,v exists.
138 FileName tmp(file.absFileName() + ",v");
139 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
140 if (tmp.isReadableFile()) {
141 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
145 // Check if RCS/*,v exists.
146 tmp = FileName(addName(addPath(onlyPath(file.absFileName()), "RCS"), file.absFileName()) + ",v");
147 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under rcs: " << tmp);
148 if (tmp.isReadableFile()) {
149 LYXERR(Debug::LYXVC, "Yes, " << file << " is under rcs.");
157 bool RCS::retrieve(FileName const & file)
159 LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file);
160 // The caller ensures that file does not exist, so no need to check that.
161 return doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()),
166 void RCS::scanMaster()
171 LYXERR(Debug::LYXVC, "LyXVC::RCS: scanMaster: " << master_);
173 ifstream ifs(master_.toFilesystemEncoding().c_str());
174 // limit the size of strings we read to avoid memory problems
178 bool read_enough = false;
180 while (!read_enough && ifs >> token) {
181 LYXERR(Debug::LYXVC, "LyXVC::scanMaster: current lex text: `"
186 else if (token == "head") {
190 tmv = rtrim(tmv, ";");
192 LYXERR(Debug::LYXVC, "LyXVC: version found to be " << tmv);
193 } else if (contains(token, "access")
194 || contains(token, "symbols")
195 || contains(token, "strict")) {
197 } else if (contains(token, "locks")) {
199 if (contains(token, ';')) {
200 locker_ = "Unlocked";
209 s1 = rtrim(tmpt, ";");
210 // tmp is now in the format <user>:<version>
211 s1 = split(s1, s2, ':');
212 // s2 is user, and s1 is version
213 if (s1 == version_) {
218 } while (!contains(tmpt, ';'));
220 } else if (token == "comment") {
221 // we don't need to read any further than this.
225 LYXERR(Debug::LYXVC, "LyXVC::scanMaster(): unexpected token");
231 void RCS::registrer(string const & msg)
233 string cmd = "ci -q -u -i -t-\"";
236 cmd += quoteName(onlyFileName(owner_->absFileName()));
237 doVCCommand(cmd, FileName(owner_->filePath()));
241 bool RCS::renameEnabled()
247 string RCS::rename(support::FileName const & /*newFile*/, string const & /*msg*/)
249 // not implemented, since a left-over file.lyx,v would be confusing.
254 bool RCS::copyEnabled()
260 string RCS::copy(support::FileName const & newFile, string const & msg)
262 // RCS has no real copy command, so we create a poor mans version
263 support::FileName const oldFile(owner_->absFileName());
264 if (!oldFile.copyTo(newFile))
266 FileName path(oldFile.onlyPath());
267 string relFile(to_utf8(newFile.relPath(path.absFileName())));
268 string cmd = "ci -q -u -i -t-\"";
271 cmd += quoteName(relFile);
272 return doVCCommand(cmd, path) ? string() : "RCS: Proceeded";
276 LyXVC::CommandResult RCS::checkIn(string const & msg, string & log)
278 int ret = doVCCommand("ci -q -u -m\"" + msg + "\" "
279 + quoteName(onlyFileName(owner_->absFileName())),
280 FileName(owner_->filePath()));
282 return LyXVC::ErrorCommand;
283 log = "RCS: Proceeded";
284 return LyXVC::VCSuccess;
288 bool RCS::checkInEnabled()
290 return owner_ && !owner_->hasReadonlyFlag();
294 bool RCS::isCheckInWithConfirmation()
296 // FIXME one day common getDiff for all backends
298 // if (getDiff(file, diff) && diff.empty())
301 TempFile tempfile("lyxvcout");
302 FileName tmpf = tempfile.name();
304 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
308 doVCCommandCall("rcsdiff " + quoteName(owner_->absFileName())
309 + " > " + quoteName(tmpf.toFilesystemEncoding()),
310 FileName(owner_->filePath()));
312 docstring diff = tmpf.fileContents("UTF-8");
321 string RCS::checkOut()
324 int ret = doVCCommand("co -q -l " + quoteName(onlyFileName(owner_->absFileName())),
325 FileName(owner_->filePath()));
326 return ret ? string() : "RCS: Proceeded";
330 bool RCS::checkOutEnabled()
332 return owner_ && owner_->hasReadonlyFlag();
336 string RCS::repoUpdate()
338 lyxerr << "Sorry, not implemented." << endl;
343 bool RCS::repoUpdateEnabled()
349 string RCS::lockingToggle()
351 //FIXME this might be actually possible, study rcs -U, rcs -L.
352 //State should be easy to get inside scanMaster.
353 //It would fix #4370 and make rcs/svn usage even more closer.
354 lyxerr << "Sorry, not implemented." << endl;
359 bool RCS::lockingToggleEnabled()
367 if (doVCCommand("co -f -u" + version_ + ' '
368 + quoteName(onlyFileName(owner_->absFileName())),
369 FileName(owner_->filePath())))
371 // We ignore changes and just reload!
377 bool RCS::isRevertWithConfirmation()
379 //FIXME owner && diff ?
386 LYXERR(Debug::LYXVC, "LyXVC: undoLast");
387 doVCCommand("rcs -o" + version_ + ' '
388 + quoteName(onlyFileName(owner_->absFileName())),
389 FileName(owner_->filePath()));
393 bool RCS::undoLastEnabled()
399 void RCS::getLog(FileName const & tmpf)
401 doVCCommand("rlog " + quoteName(onlyFileName(owner_->absFileName()))
402 + " > " + quoteName(tmpf.toFilesystemEncoding()),
403 FileName(owner_->filePath()));
407 bool RCS::toggleReadOnlyEnabled()
409 // This got broken somewhere along lfuns dispatch reorganization.
410 // reloadBuffer would be needed after this, but thats problematic
411 // since we are inside Buffer::dispatch.
417 string RCS::revisionInfo(LyXVC::RevisionInfo const info)
419 if (info == LyXVC::File)
421 // fill the rest of the attributes for a single file
422 if (rev_date_cache_.empty())
423 if (!getRevisionInfo())
428 return rev_author_cache_;
430 return rev_date_cache_;
432 return rev_time_cache_;
440 bool RCS::getRevisionInfo()
442 TempFile tempfile("lyxvcout");
443 FileName tmpf = tempfile.name();
445 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
448 doVCCommand("rlog -r " + quoteName(onlyFileName(owner_->absFileName()))
449 + " > " + quoteName(tmpf.toFilesystemEncoding()),
450 FileName(owner_->filePath()));
455 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
458 // we reached to the entry, i.e. after initial log message
460 // line with critical info, e.g:
461 //"date: 2011/07/02 11:02:54; author: sanda; state: Exp; lines: +17 -2"
466 LYXERR(Debug::LYXVC, line);
467 if (entry && prefixIs(line, "date:")) {
471 if (prefixIs(line, "revision"))
477 rev_date_cache_ = token(result, ' ', 1);
478 rev_time_cache_ = rtrim(token(result, ' ', 2), ";");
479 rev_author_cache_ = trim(token(token(result, ';', 1), ':', 1));
481 return !rev_author_cache_.empty();
484 bool RCS::prepareFileRevision(string const &revis, string & f)
487 if (!VCS::makeRCSRevision(version_, rev))
490 TempFile tempfile("lyxvcrev_" + rev + '_');
491 tempfile.setAutoRemove(false);
492 FileName tmpf = tempfile.name();
494 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
498 doVCCommand("co -p" + rev + ' '
499 + quoteName(onlyFileName(owner_->absFileName()))
500 + " > " + quoteName(tmpf.toFilesystemEncoding()),
501 FileName(owner_->filePath()));
503 if (tmpf.isFileEmpty())
506 f = tmpf.absFileName();
511 bool RCS::prepareFileRevisionEnabled()
517 /////////////////////////////////////////////////////////////////////
521 /////////////////////////////////////////////////////////////////////
523 CVS::CVS(FileName const & m, Buffer * b) : VCS(b)
525 // Here we know that the buffer file is either already in CVS or
526 // about to be registered
528 have_rev_info_ = false;
533 FileName const CVS::findFile(FileName const & file)
535 // First we look for the CVS/Entries in the same dir
536 // where we have file.
537 FileName const entries(onlyPath(file.absFileName()) + "/CVS/Entries");
538 string const tmpf = '/' + onlyFileName(file.absFileName()) + '/';
539 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under cvs in `" << entries
540 << "' for `" << tmpf << '\'');
541 if (entries.isReadableFile()) {
542 // Ok we are at least in a CVS dir. Parse the CVS/Entries
543 // and see if we can find this file. We do a fast and
545 ifstream ifs(entries.toFilesystemEncoding().c_str());
547 while (getline(ifs, line)) {
548 LYXERR(Debug::LYXVC, "\tEntries: " << line);
549 if (contains(line, tmpf))
557 void CVS::scanMaster()
559 LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n Checking: " << master_);
560 // Ok now we do the real scan...
561 ifstream ifs(master_.toFilesystemEncoding().c_str());
562 string name = onlyFileName(owner_->absFileName());
563 string tmpf = '/' + name + '/';
564 LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
566 static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
567 while (getline(ifs, line)) {
568 LYXERR(Debug::LYXVC, "\t line: " << line);
569 if (contains(line, tmpf)) {
570 // Ok extract the fields.
572 if (!regex_match(line, sm, reg)) {
573 LYXERR(Debug::LYXVC, "\t Cannot parse line. Skipping.");
577 //sm[0]; // whole matched string
579 version_ = sm.str(2);
580 string const file_date = sm.str(3);
583 //sm[5]; // tag or tagdate
584 FileName file(owner_->absFileName());
585 if (file.isReadableFile()) {
586 time_t mod = file.lastModified();
587 string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
588 LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
589 << "'\nModification date of file: `" << mod_date << '\'');
590 if (file.isReadOnly()) {
591 // readonly checkout is unlocked
594 FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
595 FileName base(addName(bdir.absFileName(),name));
596 // if base version is existent "cvs edit" was used to lock
597 vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
600 vcstatus = NOLOCKING;
608 bool CVS::retrieve(FileName const & file)
610 LYXERR(Debug::LYXVC, "LyXVC::CVS: retrieve.\n\t" << file);
611 // The caller ensures that file does not exist, so no need to check that.
612 return doVCCommandCall("cvs -q update " + quoteName(file.toFilesystemEncoding()),
613 file.onlyPath()) == 0;
617 string const CVS::getTarget(OperationMode opmode) const
621 // in client server mode CVS does not like full path operand for directory operation
622 // since LyX switches to the repo dir "." is good enough as target
625 return quoteName(onlyFileName(owner_->absFileName()));
631 docstring CVS::toString(CvsStatus status) const
635 return _("Up-to-date");
636 case LocallyModified:
637 return _("Locally Modified");
639 return _("Locally Added");
641 return _("Needs Merge");
643 return _("Needs Checkout");
645 return _("No CVS file");
647 return _("Cannot retrieve CVS status");
653 int CVS::doVCCommandWithOutput(string const & cmd, FileName const & path,
654 FileName const & output, bool reportError)
656 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
657 return doVCCommand(cmd + redirection, path, reportError);
661 int CVS::doVCCommandCallWithOutput(std::string const & cmd,
662 support::FileName const & path,
663 support::FileName const & output)
665 string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding());
666 return doVCCommandCall(cmd + redirection, path);
670 CVS::CvsStatus CVS::getStatus()
672 TempFile tempfile("lyxvout");
673 FileName tmpf = tempfile.name();
675 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
679 if (doVCCommandCallWithOutput("cvs status " + getTarget(File),
680 FileName(owner_->filePath()), tmpf)) {
684 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
685 CvsStatus status = NoCvsFile;
690 LYXERR(Debug::LYXVC, line << '\n');
691 if (prefixIs(line, "File:")) {
692 if (contains(line, "Up-to-date"))
694 else if (contains(line, "Locally Modified"))
695 status = LocallyModified;
696 else if (contains(line, "Locally Added"))
697 status = LocallyAdded;
698 else if (contains(line, "Needs Merge"))
700 else if (contains(line, "Needs Checkout"))
701 status = NeedsCheckout;
707 void CVS::getRevisionInfo()
711 have_rev_info_ = true;
712 TempFile tempfile("lyxvout");
713 FileName tmpf = tempfile.name();
715 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
719 int rc = doVCCommandCallWithOutput("cvs log -r" + version_
720 + ' ' + getTarget(File),
721 FileName(owner_->filePath()), tmpf);
723 LYXERR(Debug::LYXVC, "cvs log failed with exit code " << rc);
727 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
728 static regex const reg("date: (.*) (.*) (.*); author: (.*); state: (.*);(.*)");
733 LYXERR(Debug::LYXVC, line << '\n');
734 if (prefixIs(line, "date:")) {
736 regex_match(line, sm, reg);
737 //sm[0]; // whole matched string
738 rev_date_cache_ = sm[1];
739 rev_time_cache_ = sm[2];
740 //sm[3]; // GMT offset
741 rev_author_cache_ = sm[4];
745 if (rev_author_cache_.empty())
747 "Could not retrieve revision info for " << version_ <<
748 " of " << getTarget(File));
752 void CVS::registrer(string const & msg)
754 doVCCommand("cvs -q add -m \"" + msg + "\" "
756 FileName(owner_->filePath()));
760 bool CVS::renameEnabled()
766 string CVS::rename(support::FileName const & newFile, string const & msg)
768 // CVS has no real rename command, so we create a poor mans version
769 support::FileName const oldFile(owner_->absFileName());
770 string ret = copy(newFile, msg);
773 string cmd = "cvs -q remove -m \"" + msg + "\" " +
774 quoteName(oldFile.onlyFileName());
775 FileName path(oldFile.onlyPath());
776 return doVCCommand(cmd, path) ? string() : ret;
780 bool CVS::copyEnabled()
786 string CVS::copy(support::FileName const & newFile, string const & msg)
788 // CVS has no real copy command, so we create a poor mans version
789 support::FileName const oldFile(owner_->absFileName());
790 if (!oldFile.copyTo(newFile))
792 FileName path(oldFile.onlyPath());
793 string relFile(to_utf8(newFile.relPath(path.absFileName())));
794 string cmd("cvs -q add -m \"" + msg + "\" " + quoteName(relFile));
795 return doVCCommand(cmd, path) ? string() : "CVS: Proceeded";
799 void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
801 doVCCommandWithOutput("cvs diff " + getTarget(opmode),
802 FileName(owner_->filePath()), tmpf, false);
809 return doVCCommand("cvs -q edit " + getTarget(File),
810 FileName(owner_->filePath()));
817 return doVCCommand("cvs -q unedit " + getTarget(File),
818 FileName(owner_->filePath()));
822 int CVS::update(OperationMode opmode, FileName const & tmpf)
824 return doVCCommandWithOutput("cvs -q update "
826 FileName(owner_->filePath()), tmpf, false);
830 string CVS::scanLogFile(FileName const & f, string & status)
832 ifstream ifs(f.toFilesystemEncoding().c_str());
837 LYXERR(Debug::LYXVC, line << '\n');
839 status += line + "; ";
840 if (prefixIs(line, "C ")) {
850 LyXVC::CommandResult CVS::checkIn(string const & msg, string & log)
852 CvsStatus status = getStatus();
855 if (vcstatus != NOLOCKING)
857 return LyXVC::ErrorCommand;
858 log = "CVS: Proceeded";
859 return LyXVC::VCSuccess;
860 case LocallyModified:
862 int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" "
864 FileName(owner_->filePath()));
866 return LyXVC::ErrorCommand;
867 log = "CVS: Proceeded";
868 return LyXVC::VCSuccess;
872 frontend::Alert::error(_("Revision control error."),
873 _("The repository version is newer then the current check out.\n"
874 "You have to update from repository first or revert your changes.")) ;
877 frontend::Alert::error(_("Revision control error."),
878 bformat(_("Bad status when checking in changes.\n"
883 return LyXVC::ErrorBefore;
887 bool CVS::isLocked() const
889 FileName fn(owner_->absFileName());
891 return !fn.isReadOnly();
895 bool CVS::checkInEnabled()
897 if (vcstatus != NOLOCKING)
904 bool CVS::isCheckInWithConfirmation()
906 CvsStatus status = getStatus();
907 return status == LocallyModified || status == LocallyAdded;
911 string CVS::checkOut()
913 if (vcstatus != NOLOCKING && edit())
915 TempFile tempfile("lyxvout");
916 FileName tmpf = tempfile.name();
918 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
922 int rc = update(File, tmpf);
924 string const res = scanLogFile(tmpf, log);
926 frontend::Alert::error(_("Revision control error."),
927 bformat(_("Error when updating from repository.\n"
928 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
929 "After pressing OK, LyX will try to reopen the resolved document."),
930 from_local8bit(res)));
934 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
938 bool CVS::checkOutEnabled()
940 if (vcstatus != NOLOCKING)
947 string CVS::repoUpdate()
949 TempFile tempfile("lyxvout");
950 FileName tmpf = tempfile.name();
952 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
956 getDiff(Directory, tmpf);
957 docstring res = tmpf.fileContents("UTF-8");
959 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
960 docstring const file = from_utf8(owner_->filePath());
961 docstring text = bformat(_("There were detected changes "
962 "in the working directory:\n%1$s\n\n"
963 "Possible file conflicts must be then resolved manually "
964 "or you will need to revert back to the repository version."), file);
965 int ret = frontend::Alert::prompt(_("Changes detected"),
966 text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ..."));
968 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
969 ret = frontend::Alert::prompt(_("Changes detected"),
970 text, 0, 1, _("&Continue"), _("&Abort"));
971 hideDialogs("file", 0);
977 int rc = update(Directory, tmpf);
978 res += "Update log:\n" + tmpf.fileContents("UTF-8");
979 LYXERR(Debug::LYXVC, res);
982 string sres = scanLogFile(tmpf, log);
984 docstring const file = owner_->fileName().displayName(20);
985 frontend::Alert::error(_("Revision control error."),
986 bformat(_("Error when updating document %1$s from repository.\n"
987 "You have to manually resolve the conflicts NOW!\n'%2$s'.\n\n"
988 "After pressing OK, LyX will try to reopen the resolved document."),
989 file, from_local8bit(sres)));
993 return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
997 bool CVS::repoUpdateEnabled()
1003 string CVS::lockingToggle()
1005 lyxerr << "Sorry, not implemented." << endl;
1010 bool CVS::lockingToggleEnabled()
1016 bool CVS::isRevertWithConfirmation()
1018 CvsStatus status = getStatus();
1019 return !owner_->isClean() || status == LocallyModified || status == NeedsMerge;
1025 // Reverts to the version in CVS repository and
1026 // gets the updated version from the repository.
1027 CvsStatus status = getStatus();
1030 if (vcstatus != NOLOCKING)
1031 return 0 == unedit();
1035 case LocallyModified: {
1036 FileName f(owner_->absFileName());
1038 update(File, FileName());
1039 owner_->markClean();
1042 case LocallyAdded: {
1043 docstring const file = owner_->fileName().displayName(20);
1044 frontend::Alert::error(_("Revision control error."),
1045 bformat(_("The document %1$s is not in repository.\n"
1046 "You have to check in the first revision before you can revert."),
1051 docstring const file = owner_->fileName().displayName(20);
1052 frontend::Alert::error(_("Revision control error."),
1053 bformat(_("Cannot revert document %1$s to repository version.\n"
1054 "The status '%2$s' is unexpected."),
1055 file, toString(status)));
1063 void CVS::undoLast()
1065 // merge the current with the previous version
1066 // in a reverse patch kind of way, so that the
1067 // result is to revert the last changes.
1068 lyxerr << "Sorry, not implemented." << endl;
1072 bool CVS::undoLastEnabled()
1078 void CVS::getLog(FileName const & tmpf)
1080 doVCCommandWithOutput("cvs log " + getTarget(File),
1081 FileName(owner_->filePath()),
1086 bool CVS::toggleReadOnlyEnabled()
1092 string CVS::revisionInfo(LyXVC::RevisionInfo const info)
1094 if (!version_.empty()) {
1100 return rev_author_cache_;
1102 return rev_date_cache_;
1104 return rev_time_cache_;
1112 bool CVS::prepareFileRevision(string const & revis, string & f)
1115 if (!VCS::makeRCSRevision(version_, rev))
1118 TempFile tempfile("lyxvcrev_" + rev + '_');
1119 tempfile.setAutoRemove(false);
1120 FileName tmpf = tempfile.name();
1122 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1126 doVCCommandWithOutput("cvs update -p -r" + rev + ' '
1128 FileName(owner_->filePath()), tmpf);
1130 if (tmpf.isFileEmpty())
1133 f = tmpf.absFileName();
1138 bool CVS::prepareFileRevisionEnabled()
1144 /////////////////////////////////////////////////////////////////////
1148 /////////////////////////////////////////////////////////////////////
1150 SVN::SVN(FileName const & m, Buffer * b) : VCS(b)
1152 // Here we know that the buffer file is either already in SVN or
1153 // about to be registered
1160 FileName const SVN::findFile(FileName const & file)
1162 // First we check the existence of repository meta data.
1163 if (!VCS::checkparentdirs(file, ".svn")) {
1164 LYXERR(Debug::LYXVC, "Cannot find SVN meta data for " << file);
1168 // Now we check the status of the file.
1169 TempFile tempfile("lyxvcout");
1170 FileName tmpf = tempfile.name();
1172 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1176 string const fname = onlyFileName(file.absFileName());
1177 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under svn control for `" << fname << '\'');
1178 bool found = 0 == doVCCommandCall("svn info " + quoteName(fname)
1179 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1181 LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled"));
1182 return found ? file : FileName();
1186 void SVN::scanMaster()
1188 // vcstatus code is somewhat superflous,
1189 // until we want to implement read-only toggle for svn.
1190 vcstatus = NOLOCKING;
1191 if (checkLockMode()) {
1195 vcstatus = UNLOCKED;
1200 bool SVN::checkLockMode()
1202 TempFile tempfile("lyxvcout");
1203 FileName tmpf = tempfile.name();
1205 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1209 LYXERR(Debug::LYXVC, "Detecting locking mode...");
1210 if (doVCCommandCall("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1211 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1212 FileName(owner_->filePath())))
1215 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1219 while (ifs && !ret) {
1221 LYXERR(Debug::LYXVC, line);
1222 if (contains(line, "svn:needs-lock"))
1225 LYXERR(Debug::LYXVC, "Locking enabled: " << ret);
1233 bool SVN::isLocked() const
1235 FileName file(owner_->absFileName());
1237 return !file.isReadOnly();
1241 bool SVN::retrieve(FileName const & file)
1243 LYXERR(Debug::LYXVC, "LyXVC::SVN: retrieve.\n\t" << file);
1244 // The caller ensures that file does not exist, so no need to check that.
1245 return doVCCommandCall("svn update -q --non-interactive " + quoteName(file.onlyFileName()),
1246 file.onlyPath()) == 0;
1250 void SVN::registrer(string const & /*msg*/)
1252 doVCCommand("svn add -q " + quoteName(onlyFileName(owner_->absFileName())),
1253 FileName(owner_->filePath()));
1257 bool SVN::renameEnabled()
1263 string SVN::rename(support::FileName const & newFile, string const & msg)
1265 // svn move does not require a log message, since it does not commit.
1266 // In LyX we commit immediately afterwards, otherwise it could be
1267 // confusing to the user to have two uncommitted files.
1268 FileName path(owner_->filePath());
1269 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1270 string cmd("svn move -q " + quoteName(onlyFileName(owner_->absFileName())) +
1271 ' ' + quoteName(relFile));
1272 if (doVCCommand(cmd, path)) {
1273 cmd = "svn revert -q " +
1274 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1276 doVCCommand(cmd, path);
1277 if (newFile.exists())
1278 newFile.removeFile();
1281 vector<support::FileName> f;
1282 f.push_back(owner_->fileName());
1283 f.push_back(newFile);
1285 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1286 cmd = "svn revert -q " +
1287 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1289 doVCCommand(cmd, path);
1290 if (newFile.exists())
1291 newFile.removeFile();
1298 bool SVN::copyEnabled()
1304 string SVN::copy(support::FileName const & newFile, string const & msg)
1306 // svn copy does not require a log message, since it does not commit.
1307 // In LyX we commit immediately afterwards, otherwise it could be
1308 // confusing to the user to have an uncommitted file.
1309 FileName path(owner_->filePath());
1310 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1311 string cmd("svn copy -q " + quoteName(onlyFileName(owner_->absFileName())) +
1312 ' ' + quoteName(relFile));
1313 if (doVCCommand(cmd, path))
1315 vector<support::FileName> f(1, newFile);
1317 if (checkIn(f, msg, log) == LyXVC::VCSuccess)
1323 LyXVC::CommandResult SVN::checkIn(string const & msg, string & log)
1325 vector<support::FileName> f(1, owner_->fileName());
1326 return checkIn(f, msg, log);
1330 LyXVC::CommandResult
1331 SVN::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1333 TempFile tempfile("lyxvcout");
1334 FileName tmpf = tempfile.name();
1336 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1337 log = N_("Error: Could not generate logfile.");
1338 return LyXVC::ErrorBefore;
1342 os << "svn commit -m \"" << msg << '"';
1343 for (size_t i = 0; i < f.size(); ++i)
1344 os << ' ' << quoteName(f[i].onlyFileName());
1345 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1346 LyXVC::CommandResult ret =
1347 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1348 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1350 string res = scanLogFile(tmpf, log);
1352 frontend::Alert::error(_("Revision control error."),
1353 _("Error when committing to repository.\n"
1354 "You have to manually resolve the problem.\n"
1355 "LyX will reopen the document after you press OK."));
1356 ret = LyXVC::ErrorCommand;
1359 if (!fileLock(false, tmpf, log))
1360 ret = LyXVC::ErrorCommand;
1363 log.insert(0, "SVN: ");
1364 if (ret == LyXVC::VCSuccess && log.empty())
1365 log = "SVN: Proceeded";
1370 bool SVN::checkInEnabled()
1379 bool SVN::isCheckInWithConfirmation()
1381 // FIXME one day common getDiff and perhaps OpMode for all backends
1383 TempFile tempfile("lyxvcout");
1384 FileName tmpf = tempfile.name();
1386 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1390 doVCCommandCall("svn diff " + quoteName(owner_->absFileName())
1391 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1392 FileName(owner_->filePath()));
1394 docstring diff = tmpf.fileContents("UTF-8");
1403 // FIXME Correctly return code should be checked instead of this.
1404 // This would need another solution than just plain startscript.
1405 // Hint from Andre': QProcess::readAllStandardError()...
1406 string SVN::scanLogFile(FileName const & f, string & status)
1408 ifstream ifs(f.toFilesystemEncoding().c_str());
1413 LYXERR(Debug::LYXVC, line << '\n');
1415 status += line + "; ";
1416 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
1417 || contains(line, "Commit failed")) {
1421 if (contains(line, "svn:needs-lock")) {
1431 bool SVN::fileLock(bool lock, FileName const & tmpf, string &status)
1433 if (!locked_mode_ || (isLocked() == lock))
1436 string const arg = lock ? "lock " : "unlock ";
1437 doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName()))
1438 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1439 FileName(owner_->filePath()));
1441 // Lock error messages go unfortunately on stderr and are unreachable this way.
1442 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1446 if (!line.empty()) status += line + "; ";
1450 if (isLocked() == lock)
1454 frontend::Alert::error(_("Revision control error."),
1455 _("Error while acquiring write lock.\n"
1456 "Another user is most probably editing\n"
1457 "the current document now!\n"
1458 "Also check the access to the repository."));
1460 frontend::Alert::error(_("Revision control error."),
1461 _("Error while releasing write lock.\n"
1462 "Check the access to the repository."));
1467 string SVN::checkOut()
1469 TempFile tempfile("lyxvcout");
1470 FileName tmpf = tempfile.name();
1472 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1473 return N_("Error: Could not generate logfile.");
1476 doVCCommand("svn update --non-interactive " + quoteName(onlyFileName(owner_->absFileName()))
1477 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1478 FileName(owner_->filePath()));
1481 string const res = scanLogFile(tmpf, log);
1483 frontend::Alert::error(_("Revision control error."),
1484 bformat(_("Error when updating from repository.\n"
1485 "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n"
1486 "After pressing OK, LyX will try to reopen the resolved document."),
1487 from_local8bit(res)));
1489 fileLock(true, tmpf, log);
1491 return log.empty() ? string() : "SVN: " + log;
1495 bool SVN::checkOutEnabled()
1504 string SVN::repoUpdate()
1506 TempFile tempfile("lyxvcout");
1507 FileName tmpf = tempfile.name();
1509 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1510 return N_("Error: Could not generate logfile.");
1513 doVCCommand("svn diff " + quoteName(owner_->filePath())
1514 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1515 FileName(owner_->filePath()));
1516 docstring res = tmpf.fileContents("UTF-8");
1518 LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
1519 docstring const file = from_utf8(owner_->filePath());
1520 docstring text = bformat(_("There were detected changes "
1521 "in the working directory:\n%1$s\n\n"
1522 "In case of file conflict version of the local directory files "
1523 "will be preferred."
1524 "\n\nContinue?"), file);
1525 int ret = frontend::Alert::prompt(_("Changes detected"),
1526 text, 0, 1, _("&Yes"), _("&No"), _("View &Log ..."));
1528 dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
1529 ret = frontend::Alert::prompt(_("Changes detected"),
1530 text, 0, 1, _("&Yes"), _("&No"));
1531 hideDialogs("file", 0);
1537 // Reverting looks too harsh, see bug #6255.
1538 // doVCCommand("svn revert -R " + quoteName(owner_->filePath())
1539 // + " > " + quoteName(tmpf.toFilesystemEncoding()),
1540 // FileName(owner_->filePath()));
1541 // res = "Revert log:\n" + tmpf.fileContents("UTF-8");
1542 doVCCommand("svn update --accept mine-full " + quoteName(owner_->filePath())
1543 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1544 FileName(owner_->filePath()));
1545 res += "Update log:\n" + tmpf.fileContents("UTF-8");
1547 LYXERR(Debug::LYXVC, res);
1548 return to_utf8(res);
1552 bool SVN::repoUpdateEnabled()
1558 string SVN::lockingToggle()
1560 TempFile tempfile("lyxvcout");
1561 FileName tmpf = tempfile.name();
1563 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1564 return N_("Error: Could not generate logfile.");
1567 int ret = doVCCommand("svn proplist " + quoteName(onlyFileName(owner_->absFileName()))
1568 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1569 FileName(owner_->filePath()));
1574 string res = scanLogFile(tmpf, log);
1575 bool locking = contains(res, "svn:needs-lock");
1577 ret = doVCCommand("svn propset svn:needs-lock ON "
1578 + quoteName(onlyFileName(owner_->absFileName()))
1579 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1580 FileName(owner_->filePath()));
1582 ret = doVCCommand("svn propdel svn:needs-lock "
1583 + quoteName(onlyFileName(owner_->absFileName()))
1584 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1585 FileName(owner_->filePath()));
1589 frontend::Alert::warning(_("SVN File Locking"),
1590 (locking ? _("Locking property unset.") : _("Locking property set.")) + '\n'
1591 + _("Do not forget to commit the locking property into the repository."),
1594 return string("SVN: ") + (locking ?
1595 N_("Locking property unset.") : N_("Locking property set."));
1599 bool SVN::lockingToggleEnabled()
1607 // Reverts to the version in SVN repository and
1608 // gets the updated version from the repository.
1609 string const fil = quoteName(onlyFileName(owner_->absFileName()));
1611 if (doVCCommand("svn revert -q " + fil,
1612 FileName(owner_->filePath())))
1614 owner_->markClean();
1619 bool SVN::isRevertWithConfirmation()
1621 //FIXME owner && diff
1626 void SVN::undoLast()
1628 // merge the current with the previous version
1629 // in a reverse patch kind of way, so that the
1630 // result is to revert the last changes.
1631 lyxerr << "Sorry, not implemented." << endl;
1635 bool SVN::undoLastEnabled()
1641 string SVN::revisionInfo(LyXVC::RevisionInfo const info)
1643 if (info == LyXVC::Tree) {
1644 if (rev_tree_cache_.empty())
1645 if (!getTreeRevisionInfo())
1646 rev_tree_cache_ = "?";
1647 if (rev_tree_cache_ == "?")
1650 return rev_tree_cache_;
1653 // fill the rest of the attributes for a single file
1654 if (rev_file_cache_.empty())
1655 if (!getFileRevisionInfo())
1656 rev_file_cache_ = "?";
1660 if (rev_file_cache_ == "?")
1662 return rev_file_cache_;
1664 return rev_author_cache_;
1666 return rev_date_cache_;
1668 return rev_time_cache_;
1677 bool SVN::getFileRevisionInfo()
1679 TempFile tempfile("lyxvcout");
1680 FileName tmpf = tempfile.name();
1682 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1686 doVCCommand("svn info --xml " + quoteName(onlyFileName(owner_->absFileName()))
1687 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1688 FileName(owner_->filePath()));
1693 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1701 LYXERR(Debug::LYXVC, line);
1702 if (prefixIs(line, "<commit"))
1704 if (c && prefixIs(line, " revision=\"") && suffixIs(line, "\">")) {
1705 string l1 = subst(line, "revision=\"", "");
1706 string l2 = trim(subst(l1, "\">", ""));
1708 rev_file_cache_ = rev = l2;
1710 if (c && prefixIs(line, "<author>") && suffixIs(line, "</author>")) {
1711 string l1 = subst(line, "<author>", "");
1712 string l2 = subst(l1, "</author>", "");
1713 rev_author_cache_ = l2;
1715 if (c && prefixIs(line, "<date>") && suffixIs(line, "</date>")) {
1716 string l1 = subst(line, "<date>", "");
1717 string l2 = subst(l1, "</date>", "");
1718 l2 = split(l2, l1, 'T');
1719 rev_date_cache_ = l1;
1720 l2 = split(l2, l1, '.');
1721 rev_time_cache_ = l1;
1726 return !rev.empty();
1730 bool SVN::getTreeRevisionInfo()
1732 TempFile tempfile("lyxvcout");
1733 FileName tmpf = tempfile.name();
1735 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1739 doVCCommand("svnversion -n . > " + quoteName(tmpf.toFilesystemEncoding()),
1740 FileName(owner_->filePath()));
1745 // only first line in case something bad happens.
1746 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
1751 rev_tree_cache_ = line;
1752 return !line.empty();
1756 void SVN::getLog(FileName const & tmpf)
1758 doVCCommand("svn log " + quoteName(onlyFileName(owner_->absFileName()))
1759 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1760 FileName(owner_->filePath()));
1764 bool SVN::prepareFileRevision(string const & revis, string & f)
1766 if (!isStrInt(revis))
1769 int rev = convert<int>(revis);
1771 if (!getFileRevisionInfo())
1774 rev = convert<int>(rev_file_cache_);
1775 // go back for minus rev
1777 rev = rev + convert<int>(rev_file_cache_);
1782 string revname = convert<string>(rev);
1783 TempFile tempfile("lyxvcrev_" + revname + '_');
1784 tempfile.setAutoRemove(false);
1785 FileName tmpf = tempfile.name();
1787 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1791 doVCCommand("svn cat -r " + revname + ' '
1792 + quoteName(onlyFileName(owner_->absFileName()))
1793 + " > " + quoteName(tmpf.toFilesystemEncoding()),
1794 FileName(owner_->filePath()));
1796 if (tmpf.isFileEmpty())
1799 f = tmpf.absFileName();
1804 bool SVN::prepareFileRevisionEnabled()
1811 bool SVN::toggleReadOnlyEnabled()
1817 /////////////////////////////////////////////////////////////////////
1821 /////////////////////////////////////////////////////////////////////
1823 GIT::GIT(FileName const & m, Buffer * b) : VCS(b)
1825 // Here we know that the buffer file is either already in GIT or
1826 // about to be registered
1832 FileName const GIT::findFile(FileName const & file)
1834 // First we check the existence of repository meta data.
1835 if (!VCS::checkparentdirs(file, ".git")) {
1836 LYXERR(Debug::LYXVC, "Cannot find GIT meta data for " << file);
1840 // Now we check the status of the file.
1841 TempFile tempfile("lyxvcout");
1842 FileName tmpf = tempfile.name();
1844 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1848 string const fname = onlyFileName(file.absFileName());
1849 LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under git control for `"
1851 doVCCommandCall("git ls-files " +
1852 quoteName(fname) + " > " +
1853 quoteName(tmpf.toFilesystemEncoding()),
1856 bool found = !tmpf.isFileEmpty();
1857 LYXERR(Debug::LYXVC, "GIT control: " << (found ? "enabled" : "disabled"));
1858 return found ? file : FileName();
1862 void GIT::scanMaster()
1864 // vcstatus code is somewhat superflous,
1865 // until we want to implement read-only toggle for git.
1866 vcstatus = NOLOCKING;
1870 bool GIT::retrieve(FileName const & file)
1872 LYXERR(Debug::LYXVC, "LyXVC::GIT: retrieve.\n\t" << file);
1873 // The caller ensures that file does not exist, so no need to check that.
1874 return doVCCommandCall("git checkout -q " + quoteName(file.onlyFileName()),
1875 file.onlyPath()) == 0;
1879 void GIT::registrer(string const & /*msg*/)
1881 doVCCommand("git add " + quoteName(onlyFileName(owner_->absFileName())),
1882 FileName(owner_->filePath()));
1886 bool GIT::renameEnabled()
1892 string GIT::rename(support::FileName const & newFile, string const & msg)
1894 // git mv does not require a log message, since it does not commit.
1895 // In LyX we commit immediately afterwards, otherwise it could be
1896 // confusing to the user to have two uncommitted files.
1897 FileName path(owner_->filePath());
1898 string relFile(to_utf8(newFile.relPath(path.absFileName())));
1899 string cmd("git mv " + quoteName(onlyFileName(owner_->absFileName())) +
1900 ' ' + quoteName(relFile));
1901 if (doVCCommand(cmd, path)) {
1902 cmd = "git checkout -q " +
1903 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1905 doVCCommand(cmd, path);
1906 if (newFile.exists())
1907 newFile.removeFile();
1910 vector<support::FileName> f;
1911 f.push_back(owner_->fileName());
1912 f.push_back(newFile);
1914 if (checkIn(f, msg, log) != LyXVC::VCSuccess) {
1915 cmd = "git checkout -q " +
1916 quoteName(onlyFileName(owner_->absFileName())) + ' ' +
1918 doVCCommand(cmd, path);
1919 if (newFile.exists())
1920 newFile.removeFile();
1927 bool GIT::copyEnabled()
1933 string GIT::copy(support::FileName const & /*newFile*/, string const & /*msg*/)
1935 // git does not support copy with history preservation
1940 LyXVC::CommandResult GIT::checkIn(string const & msg, string & log)
1942 vector<support::FileName> f(1, owner_->fileName());
1943 return checkIn(f, msg, log);
1947 LyXVC::CommandResult
1948 GIT::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
1950 TempFile tempfile("lyxvcout");
1951 FileName tmpf = tempfile.name();
1953 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
1954 log = N_("Error: Could not generate logfile.");
1955 return LyXVC::ErrorBefore;
1959 os << "git commit -m \"" << msg << '"';
1960 for (size_t i = 0; i < f.size(); ++i)
1961 os << ' ' << quoteName(f[i].onlyFileName());
1962 os << " > " << quoteName(tmpf.toFilesystemEncoding());
1963 LyXVC::CommandResult ret =
1964 doVCCommand(os.str(), FileName(owner_->filePath())) ?
1965 LyXVC::ErrorCommand : LyXVC::VCSuccess;
1967 string res = scanLogFile(tmpf, log);
1969 frontend::Alert::error(_("Revision control error."),
1970 _("Error when committing to repository.\n"
1971 "You have to manually resolve the problem.\n"
1972 "LyX will reopen the document after you press OK."));
1973 ret = LyXVC::ErrorCommand;
1977 log.insert(0, "GIT: ");
1978 if (ret == LyXVC::VCSuccess && log.empty())
1979 log = "GIT: Proceeded";
1984 bool GIT::checkInEnabled()
1990 bool GIT::isCheckInWithConfirmation()
1992 // FIXME one day common getDiff and perhaps OpMode for all backends
1994 TempFile tempfile("lyxvcout");
1995 FileName tmpf = tempfile.name();
1997 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2001 doVCCommandCall("git diff " + quoteName(owner_->absFileName())
2002 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2003 FileName(owner_->filePath()));
2005 docstring diff = tmpf.fileContents("UTF-8");
2014 // FIXME Correctly return code should be checked instead of this.
2015 // This would need another solution than just plain startscript.
2016 // Hint from Andre': QProcess::readAllStandardError()...
2017 string GIT::scanLogFile(FileName const & f, string & status)
2019 ifstream ifs(f.toFilesystemEncoding().c_str());
2024 LYXERR(Debug::LYXVC, line << "\n");
2026 status += line + "; ";
2027 if (prefixIs(line, "C ") || prefixIs(line, "CU ")
2028 || contains(line, "Commit failed")) {
2038 string GIT::checkOut()
2044 bool GIT::checkOutEnabled()
2050 string GIT::repoUpdate()
2056 bool GIT::repoUpdateEnabled()
2062 string GIT::lockingToggle()
2068 bool GIT::lockingToggleEnabled()
2076 // Reverts to the version in GIT repository and
2077 // gets the updated version from the repository.
2078 string const fil = quoteName(onlyFileName(owner_->absFileName()));
2080 if (doVCCommand("git checkout -q " + fil,
2081 FileName(owner_->filePath())))
2083 owner_->markClean();
2088 bool GIT::isRevertWithConfirmation()
2090 //FIXME owner && diff
2095 void GIT::undoLast()
2097 // merge the current with the previous version
2098 // in a reverse patch kind of way, so that the
2099 // result is to revert the last changes.
2100 lyxerr << "Sorry, not implemented." << endl;
2104 bool GIT::undoLastEnabled()
2110 string GIT::revisionInfo(LyXVC::RevisionInfo const info)
2112 if (info == LyXVC::Tree) {
2113 if (rev_tree_cache_.empty())
2114 if (!getTreeRevisionInfo())
2115 rev_tree_cache_ = "?";
2116 if (rev_tree_cache_ == "?")
2119 return rev_tree_cache_;
2122 // fill the rest of the attributes for a single file
2123 if (rev_file_cache_.empty())
2124 if (!getFileRevisionInfo())
2125 rev_file_cache_ = "?";
2129 if (rev_file_cache_ == "?")
2131 return rev_file_cache_;
2133 return rev_author_cache_;
2135 return rev_date_cache_;
2137 return rev_time_cache_;
2146 bool GIT::getFileRevisionInfo()
2148 TempFile tempfile("lyxvcout");
2149 FileName tmpf = tempfile.name();
2151 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2155 doVCCommand("git log -n 1 --pretty=format:%H%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName()))
2156 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2157 FileName(owner_->filePath()));
2162 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2165 getline(ifs, rev_file_cache_);
2167 getline(ifs, rev_author_cache_);
2171 rev_time_cache_ = split(line, rev_date_cache_, ' ');
2175 return !rev_file_cache_.empty();
2179 bool GIT::getTreeRevisionInfo()
2181 TempFile tempfile("lyxvcout");
2182 FileName tmpf = tempfile.name();
2184 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2188 doVCCommand("git describe --abbrev --dirty --long > " + quoteName(tmpf.toFilesystemEncoding()),
2189 FileName(owner_->filePath()));
2194 // only first line in case something bad happens.
2195 ifstream ifs(tmpf.toFilesystemEncoding().c_str());
2196 getline(ifs, rev_tree_cache_);
2199 return !rev_tree_cache_.empty();
2203 void GIT::getLog(FileName const & tmpf)
2205 doVCCommand("git log " + quoteName(onlyFileName(owner_->absFileName()))
2206 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2207 FileName(owner_->filePath()));
2211 //at this moment we don't accept revision SHA, but just number of revision steps back
2212 //GUI and infrastucture needs to be changed first
2213 bool GIT::prepareFileRevision(string const & revis, string & f)
2215 // anything positive means we got hash, not "0" or minus revision
2218 // hash is rarely number and should be long
2219 if (isStrInt(revis) && revis.length()<20)
2220 rev = convert<int>(revis);
2222 // revision and filename
2225 // go back for "minus" revisions
2227 pointer = "HEAD~" + convert<string>(-rev);
2234 TempFile tempfile("lyxvcrev_" + revis + '_');
2235 tempfile.setAutoRemove(false);
2236 FileName tmpf = tempfile.name();
2238 LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
2242 doVCCommand("git show " + pointer + "./"
2243 + quoteName(onlyFileName(owner_->absFileName()))
2244 + " > " + quoteName(tmpf.toFilesystemEncoding()),
2245 FileName(owner_->filePath()));
2247 if (tmpf.isFileEmpty())
2250 f = tmpf.absFileName();
2255 bool GIT::prepareFileRevisionEnabled()
2261 bool GIT::toggleReadOnlyEnabled()