X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FVCBackend.cpp;h=bc47719480488acfadb71e80ff61652253c713ed;hb=66d0d61b0c269ef7d5a0d61dc3067d3172a6ac3f;hp=208161bb24a928eed815748e483273d951c5b2ae;hpb=bcbe6ae9604a907fd935e4636d3f8d910c4cfe7f;p=lyx.git diff --git a/src/VCBackend.cpp b/src/VCBackend.cpp index 208161bb24..bc47719480 100644 --- a/src/VCBackend.cpp +++ b/src/VCBackend.cpp @@ -65,6 +65,40 @@ int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError } +bool VCS::makeRCSRevision(string const &version, string &revis) const +{ + string rev = revis; + + if (isStrInt(rev)) { + int back = convert(rev); + // if positive use as the last number in the whole revision string + if (back > 0) { + string base; + rsplit(version, base , '.' ); + rev = base + "." + rev; + } + if (back == 0) + rev = version; + // we care about the last number from revision string + // in case of backward indexing + if (back < 0) { + string cur, base; + cur = rsplit(version, base , '.' ); + if (!isStrInt(cur)) + return false; + int want = convert(cur) + back; + if (want <= 0) + return false; + + rev = base + "." + convert(want); + } + } + + revis = rev; + return true; +} + + ///////////////////////////////////////////////////////////////////// // // RCS @@ -196,6 +230,33 @@ bool RCS::checkInEnabled() } +bool RCS::isCheckInWithConfirmation() +{ + // FIXME one day common getDiff for all backends + // docstring diff; + // if (getDiff(file, diff) && diff.empty()) + // return false; + + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return true; + } + + doVCCommandCall("rcsdiff " + quoteName(owner_->absFileName()) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + + docstring diff = tmpf.fileContents("UTF-8"); + tmpf.erase(); + + if (diff.empty()) + return false; + + return true; +} + + string RCS::checkOut() { owner_->markClean(); @@ -226,6 +287,9 @@ bool RCS::repoUpdateEnabled() string RCS::lockingToggle() { + //FIXME this might be actually possible, study rcs -U, rcs -L. + //State should be easy to get inside scanMaster. + //It would fix #4370 and make rcs/svn usage even more closer. lyxerr << "Sorry, not implemented." << endl; return string(); } @@ -237,13 +301,22 @@ bool RCS::lockingToggleEnabled() } -void RCS::revert() +bool RCS::revert() { - doVCCommand("co -f -u" + version_ + " " + if (doVCCommand("co -f -u" + version_ + " " + quoteName(onlyFileName(owner_->absFileName())), - FileName(owner_->filePath())); + FileName(owner_->filePath()))) + return false; // We ignore changes and just reload! owner_->markClean(); + return true; +} + + +bool RCS::isRevertWithConfirmation() +{ + //FIXME owner && diff ? + return true; } @@ -290,31 +363,8 @@ string RCS::revisionInfo(LyXVC::RevisionInfo const info) bool RCS::prepareFileRevision(string const &revis, string & f) { string rev = revis; - - if (isStrInt(rev)) { - int back = convert(rev); - // if positive use as the last number in the whole revision string - if (back > 0) { - string base; - rsplit(version_, base , '.' ); - rev = base + "." + rev; - } - if (back == 0) - rev = version_; - // we care about the last number from revision string - // in case of backward indexing - if (back < 0) { - string cur, base; - cur = rsplit(version_, base , '.' ); - if (!isStrInt(cur)) - return false; - int want = convert(cur) + back; - if (want <= 0) - return false; - - rev = base + "." + convert(want); - } - } + if (!VCS::makeRCSRevision(version_, rev)) + return false; FileName tmpf = FileName::tempName("lyxvcrev_" + rev + "_"); if (tmpf.empty()) { @@ -350,6 +400,7 @@ CVS::CVS(FileName const & m, FileName const & f) { master_ = m; file_ = f; + have_rev_info_ = false; scanMaster(); } @@ -430,7 +481,9 @@ string const CVS::getTarget(OperationMode opmode) const { switch(opmode) { case Directory: - return quoteName(owner_->filePath()); + // in client server mode CVS does not like full path operand for directory operation + // since LyX switches to the repo dir "." is good enough as target + return "."; case File: return quoteName(onlyFileName(owner_->absFileName())); } @@ -460,6 +513,23 @@ docstring CVS::toString(CvsStatus status) const } +int CVS::doVCCommandWithOutput(string const & cmd, FileName const & path, + FileName const & output, bool reportError) +{ + string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding()); + return doVCCommand(cmd + redirection, path, reportError); +} + + +int CVS::doVCCommandCallWithOutput(std::string const & cmd, + support::FileName const & path, + support::FileName const & output) +{ + string redirection = output.empty() ? "" : " > " + quoteName(output.toFilesystemEncoding()); + return doVCCommandCall(cmd + redirection, path); +} + + CVS::CvsStatus CVS::getStatus() { FileName tmpf = FileName::tempName("lyxvcout"); @@ -468,9 +538,8 @@ CVS::CvsStatus CVS::getStatus() return StatusError; } - if (doVCCommand("cvs status " + getTarget(File) - + " > " + quoteName(tmpf.toFilesystemEncoding()), - FileName(owner_->filePath()))) { + if (doVCCommandCallWithOutput("cvs status " + getTarget(File), + FileName(owner_->filePath()), tmpf)) { tmpf.removeFile(); return StatusError; } @@ -499,6 +568,51 @@ CVS::CvsStatus CVS::getStatus() return status; } +void CVS::getRevisionInfo() +{ + if (have_rev_info_) + return; + have_rev_info_ = true; + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return; + } + + int rc = doVCCommandCallWithOutput("cvs log -r" + version_ + + " " + getTarget(File), + FileName(owner_->filePath()), tmpf); + if (rc) { + tmpf.removeFile(); + LYXERR(Debug::LYXVC, "cvs log failed with exit code " << rc); + return; + } + + ifstream ifs(tmpf.toFilesystemEncoding().c_str()); + static regex const reg("date: (.*) (.*) (.*); author: (.*); state: (.*);(.*)"); + + while (ifs) { + string line; + getline(ifs, line); + LYXERR(Debug::LYXVC, line << "\n"); + if (prefixIs(line, "date:")) { + smatch sm; + regex_match(line, sm, reg); + //sm[0]; // whole matched string + rev_date_cache_ = sm[1]; + rev_time_cache_ = sm[2]; + //sm[3]; // GMT offset + rev_author_cache_ = sm[4]; + break; + } + } + tmpf.removeFile(); + if (rev_author_cache_.empty()) + LYXERR(Debug::LYXVC, + "Could not retrieve revision info for " << version_ << + " of " << getTarget(File)); +} + void CVS::registrer(string const & msg) { @@ -510,9 +624,8 @@ void CVS::registrer(string const & msg) void CVS::getDiff(OperationMode opmode, FileName const & tmpf) { - doVCCommand("cvs diff " + getTarget(opmode) - + " > " + quoteName(tmpf.toFilesystemEncoding()), - FileName(owner_->filePath()), false); + doVCCommandWithOutput("cvs diff " + getTarget(opmode), + FileName(owner_->filePath()), tmpf, false); } @@ -534,12 +647,9 @@ int CVS::unedit() int CVS::update(OperationMode opmode, FileName const & tmpf) { - string const redirection = tmpf.empty() ? "" - : " > " + quoteName(tmpf.toFilesystemEncoding()); - - return doVCCommand("cvs -q update " - + getTarget(opmode) + redirection, - FileName(owner_->filePath())); + return doVCCommandWithOutput("cvs -q update " + + getTarget(opmode), + FileName(owner_->filePath()), tmpf, false); } @@ -612,6 +722,13 @@ bool CVS::checkInEnabled() } +bool CVS::isCheckInWithConfirmation() +{ + CvsStatus status = getStatus(); + return status == LocallyModified || status == LocallyAdded; +} + + string CVS::checkOut() { if (vcstatus != NOLOCKING && edit()) @@ -625,12 +742,14 @@ string CVS::checkOut() int rc = update(File, tmpf); string log; string const res = scanLogFile(tmpf, log); - if (!res.empty()) + if (!res.empty()) { frontend::Alert::error(_("Revision control error."), bformat(_("Error when updating from repository.\n" "You have to manually resolve the conflicts NOW!\n'%1$s'.\n\n" "After pressing OK, LyX will try to reopen the resolved document."), from_local8bit(res))); + rc = 0; + } tmpf.erase(); return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log; @@ -661,8 +780,8 @@ string CVS::repoUpdate() docstring const file = from_utf8(owner_->filePath()); docstring text = bformat(_("There were detected changes " "in the working directory:\n%1$s\n\n" - "In case of file conflict you have to resolve them " - "manually or revert to repository version later."), file); + "Possible file conflicts must be then resolved manually " + "or you will need to revert back to the repository version."), file); int ret = frontend::Alert::prompt(_("Changes detected"), text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ...")); if (ret == 2 ) { @@ -679,10 +798,23 @@ string CVS::repoUpdate() int rc = update(Directory, tmpf); res += "Update log:\n" + tmpf.fileContents("UTF-8"); + LYXERR(Debug::LYXVC, res); + + string log; + string sres = scanLogFile(tmpf, log); + if (!sres.empty()) { + docstring const file = owner_->fileName().displayName(20); + frontend::Alert::error(_("Revision control error."), + bformat(_("Error when updating document %1$s from repository.\n" + "You have to manually resolve the conflicts NOW!\n'%2$s'.\n\n" + "After pressing OK, LyX will try to reopen the resolved document."), + file, from_local8bit(sres))); + rc = 0; + } + tmpf.removeFile(); - LYXERR(Debug::LYXVC, res); - return rc ? string() : "CVS: Proceeded" ; + return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log; } @@ -705,7 +837,14 @@ bool CVS::lockingToggleEnabled() } -void CVS::revert() +bool CVS::isRevertWithConfirmation() +{ + CvsStatus status = getStatus(); + return !owner_->isClean() || status == LocallyModified || status == NeedsMerge; +} + + +bool CVS::revert() { // Reverts to the version in CVS repository and // gets the updated version from the repository. @@ -713,7 +852,7 @@ void CVS::revert() switch (status) { case UpToDate: if (vcstatus != NOLOCKING) - unedit(); + return 0 == unedit(); break; case NeedsMerge: case NeedsCheckout: @@ -724,18 +863,24 @@ void CVS::revert() owner_->markClean(); break; } - case LocallyAdded: + case LocallyAdded: { + docstring const file = owner_->fileName().displayName(20); frontend::Alert::error(_("Revision control error."), - _("The current file is not in repository.\n" - "You have to check in the first revision before you can revert.")) ; - break; - default: + bformat(_("The document %1$s is not in repository.\n" + "You have to check in the first revision before you can revert."), + file)) ; + return false; + } + default: { + docstring const file = owner_->fileName().displayName(20); frontend::Alert::error(_("Revision control error."), - bformat(_("Bad status when checking in changes.\n" - "\n'%1$s'\n\n"), - toString(status))); - break; + bformat(_("Cannot revert document %1$s to repository version.\n" + "The status '%2$s' is unexpected."), + file, toString(status))); + return false; + } } + return true; } @@ -756,9 +901,9 @@ bool CVS::undoLastEnabled() void CVS::getLog(FileName const & tmpf) { - doVCCommand("cvs log " + getTarget(File) - + " > " + quoteName(tmpf.toFilesystemEncoding()), - FileName(owner_->filePath())); + doVCCommandWithOutput("cvs log " + getTarget(File), + FileName(owner_->filePath()), + tmpf); } @@ -770,21 +915,50 @@ bool CVS::toggleReadOnlyEnabled() string CVS::revisionInfo(LyXVC::RevisionInfo const info) { - if (info == LyXVC::File) - return version_; + if (!version_.empty()) { + getRevisionInfo(); + switch (info) { + case LyXVC::File: + return version_; + case LyXVC::Author: + return rev_author_cache_; + case LyXVC::Date: + return rev_date_cache_; + case LyXVC::Time: + return rev_time_cache_; + default: ; + } + } return string(); } -bool CVS::prepareFileRevision(string const &, string &) +bool CVS::prepareFileRevision(string const & revis, string & f) { - return false; + string rev = revis; + if (!VCS::makeRCSRevision(version_, rev)) + return false; + + FileName tmpf = FileName::tempName("lyxvcrev_" + rev + "_"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return false; + } + + doVCCommandWithOutput("cvs update -p -r" + rev + " " + + getTarget(File), + FileName(owner_->filePath()), tmpf); + if (tmpf.isFileEmpty()) + return false; + + f = tmpf.absFileName(); + return true; } bool CVS::prepareFileRevisionEnabled() { - return false; + return true; } @@ -928,6 +1102,30 @@ bool SVN::checkInEnabled() } +bool SVN::isCheckInWithConfirmation() +{ + // FIXME one day common getDiff and perhaps OpMode for all backends + + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return true; + } + + doVCCommandCall("svn diff " + quoteName(owner_->absFileName()) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + + docstring diff = tmpf.fileContents("UTF-8"); + tmpf.erase(); + + if (diff.empty()) + return false; + + return true; +} + + // FIXME Correctly return code should be checked instead of this. // This would need another solution than just plain startscript. // Hint from Andre': QProcess::readAllStandardError()... @@ -1127,15 +1325,24 @@ bool SVN::lockingToggleEnabled() } -void SVN::revert() +bool SVN::revert() { - // Reverts to the version in CVS repository and + // Reverts to the version in SVN repository and // gets the updated version from the repository. string const fil = quoteName(onlyFileName(owner_->absFileName())); - doVCCommand("svn revert -q " + fil, - FileName(owner_->filePath())); + if (doVCCommand("svn revert -q " + fil, + FileName(owner_->filePath()))) + return false; owner_->markClean(); + return true; +} + + +bool SVN::isRevertWithConfirmation() +{ + //FIXME owner && diff + return true; } @@ -1299,7 +1506,7 @@ bool SVN::prepareFileRevision(string const & revis, string & f) FileName tmpf = FileName::tempName("lyxvcrev_" + revname + "_"); if (tmpf.empty()) { LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); - return N_("Error: Could not generate logfile."); + return false; } doVCCommand("svn cat -r " + revname + " "