X-Git-Url: https://git.lyx.org/gitweb/?a=blobdiff_plain;f=src%2FVCBackend.cpp;h=96b71d81ea32480fcd5c727a051264d1f22bc5ea;hb=3d4076b598deb18660e50ec9c327efc3b15f15d0;hp=98417074a121dece15dc416128c5565afbd2aa48;hpb=9d99d3dbea13bde365273a6c36e087573f66a02b;p=lyx.git diff --git a/src/VCBackend.cpp b/src/VCBackend.cpp index 98417074a1..96b71d81ea 100644 --- a/src/VCBackend.cpp +++ b/src/VCBackend.cpp @@ -24,7 +24,7 @@ #include "support/filetools.h" #include "support/gettext.h" #include "support/lstrings.h" -#include "support/Path.h" +#include "support/PathChanger.h" #include "support/Systemcall.h" #include "support/regex.h" @@ -34,7 +34,6 @@ using namespace std; using namespace lyx::support; - namespace lyx { @@ -68,7 +67,7 @@ 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 @@ -99,23 +98,21 @@ bool VCS::makeRCSRevision(string const &version, string &revis) const } -bool VCS::checkparentdirs(FileName const & file, std::string const & pathname) +bool VCS::checkparentdirs(FileName const & file, std::string const & vcsdir) { FileName dirname = file.onlyPath(); - FileName tocheck = FileName(addName(dirname.absFileName(),pathname)); - LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName()); - bool result = tocheck.exists(); - while ( !result && !dirname.empty() ) { + do { + FileName tocheck = FileName(addName(dirname.absFileName(), vcsdir)); + LYXERR(Debug::LYXVC, "check file: " << tocheck.absFileName()); + if (tocheck.exists()) + return true; //this construct because of #8295 dirname = FileName(dirname.absFileName()).parentPath(); - LYXERR(Debug::LYXVC, "check directory: " << dirname.absFileName()); - tocheck = FileName(addName(dirname.absFileName(),pathname)); - result = tocheck.exists(); - } - return result; + } while (!dirname.empty()); + return false; } - + ///////////////////////////////////////////////////////////////////// // // RCS @@ -124,6 +121,8 @@ bool VCS::checkparentdirs(FileName const & file, std::string const & pathname) RCS::RCS(FileName const & m, Buffer * b) : VCS(b) { + // Here we know that the buffer file is either already in RCS or + // about to be registered master_ = m; scanMaster(); } @@ -154,7 +153,7 @@ FileName const RCS::findFile(FileName const & file) bool RCS::retrieve(FileName const & file) { LYXERR(Debug::LYXVC, "LyXVC::RCS: retrieve.\n\t" << file); - // The caller ensures that file does not exists, so no need to check that. + // The caller ensures that file does not exist, so no need to check that. return doVCCommandCall("co -q -r " + quoteName(file.toFilesystemEncoding()), FileName()) == 0; } @@ -233,12 +232,50 @@ void RCS::registrer(string const & msg) } -string RCS::checkIn(string const & msg) +bool RCS::renameEnabled() +{ + return false; +} + + +string RCS::rename(support::FileName const & /*newFile*/, string const & /*msg*/) +{ + // not implemented, since a left-over file.lyx,v would be confusing. + return string(); +} + + +bool RCS::copyEnabled() +{ + return true; +} + + +string RCS::copy(support::FileName const & newFile, string const & msg) +{ + // RCS has no real copy command, so we create a poor mans version + support::FileName const oldFile(owner_->absFileName()); + if (!oldFile.copyTo(newFile)) + return string(); + FileName path(oldFile.onlyPath()); + string relFile(to_utf8(newFile.relPath(path.absFileName()))); + string cmd = "ci -q -u -i -t-\""; + cmd += msg; + cmd += "\" "; + cmd += quoteName(relFile); + return doVCCommand(cmd, path) ? string() : "RCS: Proceeded"; +} + + +LyXVC::CommandResult RCS::checkIn(string const & msg, string & log) { int ret = doVCCommand("ci -q -u -m\"" + msg + "\" " + quoteName(onlyFileName(owner_->absFileName())), FileName(owner_->filePath())); - return ret ? string() : "RCS: Proceeded"; + if (ret) + return LyXVC::ErrorCommand; + log = "RCS: Proceeded"; + return LyXVC::VCSuccess; } @@ -266,7 +303,7 @@ bool RCS::isCheckInWithConfirmation() FileName(owner_->filePath())); docstring diff = tmpf.fileContents("UTF-8"); - tmpf.erase(); + tmpf.removeFile(); if (diff.empty()) return false; @@ -475,6 +512,8 @@ bool RCS::prepareFileRevisionEnabled() CVS::CVS(FileName const & m, Buffer * b) : VCS(b) { + // Here we know that the buffer file is either already in CVS or + // about to be registered master_ = m; have_rev_info_ = false; scanMaster(); @@ -557,7 +596,7 @@ void CVS::scanMaster() bool CVS::retrieve(FileName const & file) { LYXERR(Debug::LYXVC, "LyXVC::CVS: retrieve.\n\t" << file); - // The caller ensures that file does not exists, so no need to check that. + // The caller ensures that file does not exist, so no need to check that. return doVCCommandCall("cvs -q update " + quoteName(file.toFilesystemEncoding()), file.onlyPath()) == 0; } @@ -708,6 +747,45 @@ void CVS::registrer(string const & msg) } +bool CVS::renameEnabled() +{ + return true; +} + + +string CVS::rename(support::FileName const & newFile, string const & msg) +{ + // CVS has no real rename command, so we create a poor mans version + support::FileName const oldFile(owner_->absFileName()); + string ret = copy(newFile, msg); + if (ret.empty()) + return ret; + string cmd = "cvs -q remove -m \"" + msg + "\" " + + quoteName(oldFile.onlyFileName()); + FileName path(oldFile.onlyPath()); + return doVCCommand(cmd, path) ? string() : ret; +} + + +bool CVS::copyEnabled() +{ + return true; +} + + +string CVS::copy(support::FileName const & newFile, string const & msg) +{ + // CVS has no real copy command, so we create a poor mans version + support::FileName const oldFile(owner_->absFileName()); + if (!oldFile.copyTo(newFile)) + return string(); + FileName path(oldFile.onlyPath()); + string relFile(to_utf8(newFile.relPath(path.absFileName()))); + string cmd("cvs -q add -m \"" + msg + "\" " + quoteName(relFile)); + return doVCCommand(cmd, path) ? string() : "CVS: Proceeded"; +} + + void CVS::getDiff(OperationMode opmode, FileName const & tmpf) { doVCCommandWithOutput("cvs diff " + getTarget(opmode), @@ -757,22 +835,27 @@ string CVS::scanLogFile(FileName const & f, string & status) ifs.close(); return string(); } - - -string CVS::checkIn(string const & msg) + + +LyXVC::CommandResult CVS::checkIn(string const & msg, string & log) { CvsStatus status = getStatus(); switch (status) { case UpToDate: if (vcstatus != NOLOCKING) - unedit(); - return "CVS: Proceeded"; + if (unedit()) + return LyXVC::ErrorCommand; + log = "CVS: Proceeded"; + return LyXVC::VCSuccess; case LocallyModified: case LocallyAdded: { int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" " + getTarget(File), FileName(owner_->filePath())); - return rc ? string() : "CVS: Proceeded"; + if (rc) + return LyXVC::ErrorCommand; + log = "CVS: Proceeded"; + return LyXVC::VCSuccess; } case NeedsMerge: case NeedsCheckout: @@ -787,7 +870,7 @@ string CVS::checkIn(string const & msg) toString(status))); break; } - return string(); + return LyXVC::ErrorBefore; } @@ -837,7 +920,7 @@ string CVS::checkOut() rc = 0; } - tmpf.erase(); + tmpf.removeFile(); return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log; } @@ -1056,6 +1139,8 @@ bool CVS::prepareFileRevisionEnabled() SVN::SVN(FileName const & m, Buffer * b) : VCS(b) { + // Here we know that the buffer file is either already in SVN or + // about to be registered master_ = m; locked_mode_ = 0; scanMaster(); @@ -1082,6 +1167,7 @@ FileName const SVN::findFile(FileName const & file) bool found = 0 == doVCCommandCall("svn info " + quoteName(fname) + " > " + quoteName(tmpf.toFilesystemEncoding()), file.onlyPath()); + tmpf.removeFile(); LYXERR(Debug::LYXVC, "SVN control: " << (found ? "enabled" : "disabled")); return found ? file : FileName(); } @@ -1089,15 +1175,14 @@ FileName const SVN::findFile(FileName const & file) void SVN::scanMaster() { - // vcstatus code is somewhat superflous, until we want - // to implement read-only toggle for svn. + // vcstatus code is somewhat superflous, + // until we want to implement read-only toggle for svn. vcstatus = NOLOCKING; if (checkLockMode()) { - if (isLocked()) { + if (isLocked()) vcstatus = LOCKED; - } else { + else vcstatus = UNLOCKED; - } } } @@ -1145,7 +1230,7 @@ bool SVN::isLocked() const bool SVN::retrieve(FileName const & file) { LYXERR(Debug::LYXVC, "LyXVC::SVN: retrieve.\n\t" << file); - // The caller ensures that file does not exists, so no need to check that. + // The caller ensures that file does not exist, so no need to check that. return doVCCommandCall("svn update -q --non-interactive " + quoteName(file.onlyFileName()), file.onlyPath()) == 0; } @@ -1158,31 +1243,116 @@ void SVN::registrer(string const & /*msg*/) } -string SVN::checkIn(string const & msg) +bool SVN::renameEnabled() +{ + return true; +} + + +string SVN::rename(support::FileName const & newFile, string const & msg) +{ + // svn move does not require a log message, since it does not commit. + // In LyX we commit immediately afterwards, otherwise it could be + // confusing to the user to have two uncommitted files. + FileName path(owner_->filePath()); + string relFile(to_utf8(newFile.relPath(path.absFileName()))); + string cmd("svn move -q " + quoteName(onlyFileName(owner_->absFileName())) + + ' ' + quoteName(relFile)); + if (doVCCommand(cmd, path)) { + cmd = "svn revert -q " + + quoteName(onlyFileName(owner_->absFileName())) + ' ' + + quoteName(relFile); + doVCCommand(cmd, path); + if (newFile.exists()) + newFile.removeFile(); + return string(); + } + vector f; + f.push_back(owner_->fileName()); + f.push_back(newFile); + string log; + if (checkIn(f, msg, log) != LyXVC::VCSuccess) { + cmd = "svn revert -q " + + quoteName(onlyFileName(owner_->absFileName())) + ' ' + + quoteName(relFile); + doVCCommand(cmd, path); + if (newFile.exists()) + newFile.removeFile(); + return string(); + } + return log; +} + + +bool SVN::copyEnabled() +{ + return true; +} + + +string SVN::copy(support::FileName const & newFile, string const & msg) +{ + // svn copy does not require a log message, since it does not commit. + // In LyX we commit immediately afterwards, otherwise it could be + // confusing to the user to have an uncommitted file. + FileName path(owner_->filePath()); + string relFile(to_utf8(newFile.relPath(path.absFileName()))); + string cmd("svn copy -q " + quoteName(onlyFileName(owner_->absFileName())) + + ' ' + quoteName(relFile)); + if (doVCCommand(cmd, path)) + return string(); + vector f(1, newFile); + string log; + if (checkIn(f, msg, log) == LyXVC::VCSuccess) + return log; + return string(); +} + + +LyXVC::CommandResult SVN::checkIn(string const & msg, string & log) +{ + vector f(1, owner_->fileName()); + return checkIn(f, msg, log); +} + + +LyXVC::CommandResult +SVN::checkIn(vector const & f, string const & msg, string & log) { FileName tmpf = FileName::tempName("lyxvcout"); if (tmpf.empty()){ LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); - return N_("Error: Could not generate logfile."); + log = N_("Error: Could not generate logfile."); + return LyXVC::ErrorBefore; } - doVCCommand("svn commit -m \"" + msg + "\" " - + quoteName(onlyFileName(owner_->absFileName())) - + " > " + quoteName(tmpf.toFilesystemEncoding()), - FileName(owner_->filePath())); + ostringstream os; + os << "svn commit -m \"" << msg << '"'; + for (size_t i = 0; i < f.size(); ++i) + os << ' ' << quoteName(f[i].onlyFileName()); + os << " > " << quoteName(tmpf.toFilesystemEncoding()); + LyXVC::CommandResult ret = + doVCCommand(os.str(), FileName(owner_->filePath())) ? + LyXVC::ErrorCommand : LyXVC::VCSuccess; - string log; string res = scanLogFile(tmpf, log); - if (!res.empty()) + if (!res.empty()) { frontend::Alert::error(_("Revision control error."), _("Error when committing to repository.\n" "You have to manually resolve the problem.\n" "LyX will reopen the document after you press OK.")); + ret = LyXVC::ErrorCommand; + } else - fileLock(false, tmpf, log); + if (!fileLock(false, tmpf, log)) + ret = LyXVC::ErrorCommand; - tmpf.erase(); - return log.empty() ? string() : "SVN: " + log; + tmpf.removeFile(); + if (!log.empty()) + log.insert(0, "SVN: "); + if (ret == LyXVC::VCSuccess && log.empty()) + log = "SVN: Proceeded"; + return ret; } @@ -1210,7 +1380,7 @@ bool SVN::isCheckInWithConfirmation() FileName(owner_->filePath())); docstring diff = tmpf.fileContents("UTF-8"); - tmpf.erase(); + tmpf.removeFile(); if (diff.empty()) return false; @@ -1247,10 +1417,10 @@ string SVN::scanLogFile(FileName const & f, string & status) } -void SVN::fileLock(bool lock, FileName const & tmpf, string &status) +bool SVN::fileLock(bool lock, FileName const & tmpf, string &status) { if (!locked_mode_ || (isLocked() == lock)) - return; + return true; string const arg = lock ? "lock " : "unlock "; doVCCommand("svn "+ arg + quoteName(onlyFileName(owner_->absFileName())) @@ -1266,16 +1436,20 @@ void SVN::fileLock(bool lock, FileName const & tmpf, string &status) } ifs.close(); - if (!isLocked() && lock) + if (isLocked() == lock) + return true; + + if (lock) frontend::Alert::error(_("Revision control error."), _("Error while acquiring write lock.\n" "Another user is most probably editing\n" "the current document now!\n" "Also check the access to the repository.")); - if (isLocked() && !lock) + else frontend::Alert::error(_("Revision control error."), _("Error while releasing write lock.\n" "Check the access to the repository.")); + return false; } @@ -1302,7 +1476,7 @@ string SVN::checkOut() fileLock(true, tmpf, log); - tmpf.erase(); + tmpf.removeFile(); return log.empty() ? string() : "SVN: " + log; } @@ -1345,7 +1519,7 @@ string SVN::repoUpdate() hideDialogs("file", 0); } if (ret == 1 ) { - tmpf.erase(); + tmpf.removeFile(); return string(); } } @@ -1361,7 +1535,7 @@ string SVN::repoUpdate() res += "Update log:\n" + tmpf.fileContents("UTF-8"); LYXERR(Debug::LYXVC, res); - tmpf.erase(); + tmpf.removeFile(); return to_utf8(res); } @@ -1402,13 +1576,14 @@ string SVN::lockingToggle() if (ret) return string(); - tmpf.erase(); - frontend::Alert::warning(_("VCN File Locking"), + tmpf.removeFile(); + frontend::Alert::warning(_("SVN File Locking"), (locking ? _("Locking property unset.") : _("Locking property set.")) + "\n" + _("Do not forget to commit the locking property into the repository."), true); - return string("SVN: ") + N_("Locking property set."); + return string("SVN: ") + (locking ? + N_("Locking property unset.") : N_("Locking property set.")); } @@ -1457,13 +1632,13 @@ bool SVN::undoLastEnabled() string SVN::revisionInfo(LyXVC::RevisionInfo const info) { if (info == LyXVC::Tree) { - if (rev_tree_cache_.empty()) - if (!getTreeRevisionInfo()) - rev_tree_cache_ = "?"; - if (rev_tree_cache_ == "?") - return string(); + if (rev_tree_cache_.empty()) + if (!getTreeRevisionInfo()) + rev_tree_cache_ = "?"; + if (rev_tree_cache_ == "?") + return string(); - return rev_tree_cache_; + return rev_tree_cache_; } // fill the rest of the attributes for a single file @@ -1538,7 +1713,7 @@ bool SVN::getFileRevisionInfo() } ifs.close(); - tmpf.erase(); + tmpf.removeFile(); return !rev.empty(); } @@ -1562,7 +1737,7 @@ bool SVN::getTreeRevisionInfo() string line; getline(ifs, line); ifs.close(); - tmpf.erase(); + tmpf.removeFile(); rev_tree_cache_ = line; return !line.empty(); @@ -1627,4 +1802,450 @@ bool SVN::toggleReadOnlyEnabled() } +///////////////////////////////////////////////////////////////////// +// +// GIT +// +///////////////////////////////////////////////////////////////////// + +GIT::GIT(FileName const & m, Buffer * b) : VCS(b) +{ + // Here we know that the buffer file is either already in GIT or + // about to be registered + master_ = m; + scanMaster(); +} + + +FileName const GIT::findFile(FileName const & file) +{ + // First we check the existence of repository meta data. + if (!VCS::checkparentdirs(file, ".git")) { + LYXERR(Debug::LYXVC, "Cannot find GIT meta data for " << file); + return FileName(); + } + + // Now we check the status of the file. + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return FileName(); + } + + string const fname = onlyFileName(file.absFileName()); + LYXERR(Debug::LYXVC, "LyXVC: Checking if file is under git control for `" + << fname << '\''); + doVCCommandCall("git ls-files " + + quoteName(fname) + " > " + + quoteName(tmpf.toFilesystemEncoding()), + file.onlyPath()); + bool found = !tmpf.isFileEmpty(); + tmpf.removeFile(); + LYXERR(Debug::LYXVC, "GIT control: " << (found ? "enabled" : "disabled")); + return found ? file : FileName(); +} + + +void GIT::scanMaster() +{ + // vcstatus code is somewhat superflous, + // until we want to implement read-only toggle for git. + vcstatus = NOLOCKING; +} + + +bool GIT::retrieve(FileName const & file) +{ + LYXERR(Debug::LYXVC, "LyXVC::GIT: retrieve.\n\t" << file); + // The caller ensures that file does not exist, so no need to check that. + return doVCCommandCall("git checkout -q " + quoteName(file.onlyFileName()), + file.onlyPath()) == 0; +} + + +void GIT::registrer(string const & /*msg*/) +{ + doVCCommand("git add " + quoteName(onlyFileName(owner_->absFileName())), + FileName(owner_->filePath())); +} + + +bool GIT::renameEnabled() +{ + return true; +} + + +string GIT::rename(support::FileName const & newFile, string const & msg) +{ + // git mv does not require a log message, since it does not commit. + // In LyX we commit immediately afterwards, otherwise it could be + // confusing to the user to have two uncommitted files. + FileName path(owner_->filePath()); + string relFile(to_utf8(newFile.relPath(path.absFileName()))); + string cmd("git mv " + quoteName(onlyFileName(owner_->absFileName())) + + ' ' + quoteName(relFile)); + if (doVCCommand(cmd, path)) { + cmd = "git checkout -q " + + quoteName(onlyFileName(owner_->absFileName())) + ' ' + + quoteName(relFile); + doVCCommand(cmd, path); + if (newFile.exists()) + newFile.removeFile(); + return string(); + } + vector f; + f.push_back(owner_->fileName()); + f.push_back(newFile); + string log; + if (checkIn(f, msg, log) != LyXVC::VCSuccess) { + cmd = "git checkout -q " + + quoteName(onlyFileName(owner_->absFileName())) + ' ' + + quoteName(relFile); + doVCCommand(cmd, path); + if (newFile.exists()) + newFile.removeFile(); + return string(); + } + return log; +} + + +bool GIT::copyEnabled() +{ + return false; +} + + +string GIT::copy(support::FileName const & /*newFile*/, string const & /*msg*/) +{ + // git does not support copy with history preservation + return string(); +} + + +LyXVC::CommandResult GIT::checkIn(string const & msg, string & log) +{ + vector f(1, owner_->fileName()); + return checkIn(f, msg, log); +} + + +LyXVC::CommandResult +GIT::checkIn(vector const & f, string const & msg, string & log) +{ + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()){ + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + log = N_("Error: Could not generate logfile."); + return LyXVC::ErrorBefore; + } + + ostringstream os; + os << "git commit -m \"" << msg << '"'; + for (size_t i = 0; i < f.size(); ++i) + os << ' ' << quoteName(f[i].onlyFileName()); + os << " > " << quoteName(tmpf.toFilesystemEncoding()); + LyXVC::CommandResult ret = + doVCCommand(os.str(), FileName(owner_->filePath())) ? + LyXVC::ErrorCommand : LyXVC::VCSuccess; + + string res = scanLogFile(tmpf, log); + if (!res.empty()) { + frontend::Alert::error(_("Revision control error."), + _("Error when committing to repository.\n" + "You have to manually resolve the problem.\n" + "LyX will reopen the document after you press OK.")); + ret = LyXVC::ErrorCommand; + } + + tmpf.removeFile(); + if (!log.empty()) + log.insert(0, "GIT: "); + if (ret == LyXVC::VCSuccess && log.empty()) + log = "GIT: Proceeded"; + return ret; +} + + +bool GIT::checkInEnabled() +{ + return true; +} + + +bool GIT::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("git diff " + quoteName(owner_->absFileName()) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + + docstring diff = tmpf.fileContents("UTF-8"); + tmpf.removeFile(); + + 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()... +string GIT::scanLogFile(FileName const & f, string & status) +{ + ifstream ifs(f.toFilesystemEncoding().c_str()); + string line; + + while (ifs) { + getline(ifs, line); + LYXERR(Debug::LYXVC, line << "\n"); + if (!line.empty()) + status += line + "; "; + if (prefixIs(line, "C ") || prefixIs(line, "CU ") + || contains(line, "Commit failed")) { + ifs.close(); + return line; + } + } + ifs.close(); + return string(); +} + + +string GIT::checkOut() +{ + return string(); +} + + +bool GIT::checkOutEnabled() +{ + return false; +} + + +string GIT::repoUpdate() +{ + return string(); +} + + +bool GIT::repoUpdateEnabled() +{ + return false; +} + + +string GIT::lockingToggle() +{ + return string(); +} + + +bool GIT::lockingToggleEnabled() +{ + return false; +} + + +bool GIT::revert() +{ + // Reverts to the version in GIT repository and + // gets the updated version from the repository. + string const fil = quoteName(onlyFileName(owner_->absFileName())); + + if (doVCCommand("git checkout -q " + fil, + FileName(owner_->filePath()))) + return false; + owner_->markClean(); + return true; +} + + +bool GIT::isRevertWithConfirmation() +{ + //FIXME owner && diff + return true; +} + + +void GIT::undoLast() +{ + // merge the current with the previous version + // in a reverse patch kind of way, so that the + // result is to revert the last changes. + lyxerr << "Sorry, not implemented." << endl; +} + + +bool GIT::undoLastEnabled() +{ + return false; +} + + +string GIT::revisionInfo(LyXVC::RevisionInfo const info) +{ + if (info == LyXVC::Tree) { + if (rev_tree_cache_.empty()) + if (!getTreeRevisionInfo()) + rev_tree_cache_ = "?"; + if (rev_tree_cache_ == "?") + return string(); + + return rev_tree_cache_; + } + + // fill the rest of the attributes for a single file + if (rev_file_cache_.empty()) + if (!getFileRevisionInfo()) + rev_file_cache_ = "?"; + + switch (info) { + case LyXVC::File: + if (rev_file_cache_ == "?") + return string(); + return rev_file_cache_; + 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 GIT::getFileRevisionInfo() +{ + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return false; + } + + doVCCommand("git log -n 1 --pretty=format:%H%n%an%n%ai " + quoteName(onlyFileName(owner_->absFileName())) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + + if (tmpf.empty()) + return false; + + ifstream ifs(tmpf.toFilesystemEncoding().c_str()); + + if (ifs) + getline(ifs, rev_file_cache_); + if (ifs) + getline(ifs, rev_author_cache_); + if (ifs) { + string line; + getline(ifs, line); + rev_time_cache_ = split(line, rev_date_cache_, ' '); + } + + ifs.close(); + tmpf.removeFile(); + return !rev_file_cache_.empty(); +} + + +bool GIT::getTreeRevisionInfo() +{ + FileName tmpf = FileName::tempName("lyxvcout"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return false; + } + + doVCCommand("git describe --abbrev --dirty --long > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + + if (tmpf.empty()) + return false; + + // only first line in case something bad happens. + ifstream ifs(tmpf.toFilesystemEncoding().c_str()); + getline(ifs, rev_tree_cache_); + ifs.close(); + tmpf.removeFile(); + + return !rev_tree_cache_.empty(); +} + + +void GIT::getLog(FileName const & tmpf) +{ + doVCCommand("git log " + quoteName(onlyFileName(owner_->absFileName())) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); +} + + +//at this moment we don't accept revision SHA, but just number of revision steps back +//GUI and infrastucture needs to be changed first +bool GIT::prepareFileRevision(string const & revis, string & f) +{ + // anything positive means we got hash, not "0" or minus revision + int rev = 1; + + // hash is rarely number and should be long + if (isStrInt(revis) && revis.length()<20) + rev = convert(revis); + + // revision and filename + string pointer; + + // go back for "minus" revisions + if (rev <= 0) + pointer = "HEAD~" + convert(-rev); + // normal hash + else + pointer = revis; + + pointer += ":"; + + FileName tmpf = FileName::tempName("lyxvcrev_" + revis + "_"); + if (tmpf.empty()) { + LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf); + return false; + } + + doVCCommand("git show " + pointer + "./" + + quoteName(onlyFileName(owner_->absFileName())) + + " > " + quoteName(tmpf.toFilesystemEncoding()), + FileName(owner_->filePath())); + if (tmpf.isFileEmpty()) + return false; + + f = tmpf.absFileName(); + return true; +} + + +bool GIT::prepareFileRevisionEnabled() +{ + return true; +} + + +bool GIT::toggleReadOnlyEnabled() +{ + return false; +} + + } // namespace lyx