* implementation of status check and use it for checkIn and revert.

helps the user to avoid errors and leads to more informative messages.
* implementation of diff and use it for the repoUpdate operation.
* add the check for merge conflicts in checkOut.


git-svn-id: svn://svn.lyx.org/lyx/lyx-devel/trunk@35813 a592a061-630c-0410-9148-cb99ea01b6c8
This commit is contained in:
Stephan Witt 2010-10-25 05:37:04 +00:00
parent ba19e8b5dd
commit bcbe6ae960
2 changed files with 316 additions and 55 deletions

View File

@ -47,7 +47,7 @@ int VCS::doVCCommandCall(string const & cmd, FileName const & path)
}
int VCS::doVCCommand(string const & cmd, FileName const & path)
int VCS::doVCCommand(string const & cmd, FileName const & path, bool reportError)
{
if (owner_)
owner_->setBusy(true);
@ -56,7 +56,7 @@ int VCS::doVCCommand(string const & cmd, FileName const & path)
if (owner_)
owner_->setBusy(false);
if (ret)
if (ret && reportError)
frontend::Alert::error(_("Revision control error."),
bformat(_("Some problem occured while running the command:\n"
"'%1$s'."),
@ -383,7 +383,8 @@ void CVS::scanMaster()
LYXERR(Debug::LYXVC, "LyXVC::CVS: scanMaster. \n Checking: " << master_);
// Ok now we do the real scan...
ifstream ifs(master_.toFilesystemEncoding().c_str());
string tmpf = '/' + onlyFileName(file_.absFileName()) + '/';
string name = onlyFileName(file_.absFileName());
string tmpf = '/' + name + '/';
LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
string line;
static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
@ -402,21 +403,22 @@ void CVS::scanMaster()
//sm[4]; // options
//sm[5]; // tag or tagdate
// FIXME: must double check file is stattable/existing
time_t mod = file_.lastModified();
string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
<< "'\nModification date of file: `" << mod_date << '\'');
//FIXME this whole locking bussiness is not working under cvs and the machinery
// conforms to the ci usage, not cvs.
if (file_date == mod_date) {
locker_ = "Unlocked";
vcstatus = UNLOCKED;
if (file_.isReadableFile()) {
time_t mod = file_.lastModified();
string mod_date = rtrim(asctime(gmtime(&mod)), "\n");
LYXERR(Debug::LYXVC, "Date in Entries: `" << file_date
<< "'\nModification date of file: `" << mod_date << '\'');
if (file_.isReadOnly()) {
// readonly checkout is unlocked
vcstatus = UNLOCKED;
} else {
FileName bdir(addPath(master_.onlyPath().absFileName(),"Base"));
FileName base(addName(bdir.absFileName(),name));
// if base version is existent "cvs edit" was used to lock
vcstatus = base.isReadableFile() ? LOCKED : NOLOCKING;
}
} else {
// Here we should also do some more checking
// to see if there are conflicts or not.
locker_ = "Locked";
vcstatus = LOCKED;
vcstatus = NOLOCKING;
}
break;
}
@ -424,64 +426,269 @@ void CVS::scanMaster()
}
void CVS::registrer(string const & msg)
string const CVS::getTarget(OperationMode opmode) const
{
doVCCommand("cvs -q add -m \"" + msg + "\" "
+ quoteName(onlyFileName(owner_->absFileName())),
FileName(owner_->filePath()));
switch(opmode) {
case Directory:
return quoteName(owner_->filePath());
case File:
return quoteName(onlyFileName(owner_->absFileName()));
}
return string();
}
docstring CVS::toString(CvsStatus status) const
{
switch (status) {
case UpToDate:
return _("Up-to-date");
case LocallyModified:
return _("Locally Modified");
case LocallyAdded:
return _("Locally Added");
case NeedsMerge:
return _("Needs Merge");
case NeedsCheckout:
return _("Needs Checkout");
case NoCvsFile:
return _("No CVS file");
case StatusError:
return _("Cannot retrieve CVS status");
}
return 0;
}
CVS::CvsStatus CVS::getStatus()
{
FileName tmpf = FileName::tempName("lyxvcout");
if (tmpf.empty()) {
LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
return StatusError;
}
if (doVCCommand("cvs status " + getTarget(File)
+ " > " + quoteName(tmpf.toFilesystemEncoding()),
FileName(owner_->filePath()))) {
tmpf.removeFile();
return StatusError;
}
ifstream ifs(tmpf.toFilesystemEncoding().c_str());
CvsStatus status = NoCvsFile;
while (ifs) {
string line;
getline(ifs, line);
LYXERR(Debug::LYXVC, line << "\n");
if (prefixIs(line, "File:")) {
if (contains(line, "Up-to-date"))
status = UpToDate;
else if (contains(line, "Locally Modified"))
status = LocallyModified;
else if (contains(line, "Locally Added"))
status = LocallyAdded;
else if (contains(line, "Needs Merge"))
status = NeedsMerge;
else if (contains(line, "Needs Checkout"))
status = NeedsCheckout;
}
}
tmpf.removeFile();
return status;
}
void CVS::registrer(string const & msg)
{
doVCCommand("cvs -q add -m \"" + msg + "\" "
+ getTarget(File),
FileName(owner_->filePath()));
}
void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
{
doVCCommand("cvs diff " + getTarget(opmode)
+ " > " + quoteName(tmpf.toFilesystemEncoding()),
FileName(owner_->filePath()), false);
}
int CVS::edit()
{
vcstatus = LOCKED;
return doVCCommand("cvs -q edit " + getTarget(File),
FileName(owner_->filePath()));
}
int CVS::unedit()
{
vcstatus = UNLOCKED;
return doVCCommand("cvs -q unedit " + getTarget(File),
FileName(owner_->filePath()));
}
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()));
}
string CVS::scanLogFile(FileName const & f, string & status)
{
ifstream ifs(f.toFilesystemEncoding().c_str());
while (ifs) {
string line;
getline(ifs, line);
LYXERR(Debug::LYXVC, line << "\n");
if (!line.empty())
status += line + "; ";
if (prefixIs(line, "C ")) {
ifs.close();
return line;
}
}
ifs.close();
return string();
}
string CVS::checkIn(string const & msg)
{
int ret = doVCCommand("cvs -q commit -m \"" + msg + "\" "
+ quoteName(onlyFileName(owner_->absFileName())),
CvsStatus status = getStatus();
switch (status) {
case UpToDate:
if (vcstatus != NOLOCKING)
unedit();
return "CVS: Proceeded";
case LocallyModified:
case LocallyAdded: {
int rc = doVCCommand("cvs -q commit -m \"" + msg + "\" "
+ getTarget(File),
FileName(owner_->filePath()));
return ret ? string() : "CVS: Proceeded";
return rc ? string() : "CVS: Proceeded";
}
case NeedsMerge:
case NeedsCheckout:
frontend::Alert::error(_("Revision control error."),
_("The repository version is newer then the current check out.\n"
"You have to update from repository first or revert your changes.")) ;
break;
default:
frontend::Alert::error(_("Revision control error."),
bformat(_("Bad status when checking in changes.\n"
"\n'%1$s'\n\n"),
toString(status)));
break;
}
return string();
}
bool CVS::isLocked() const
{
FileName fn(owner_->absFileName());
fn.refresh();
return !fn.isReadOnly();
}
bool CVS::checkInEnabled()
{
return !owner_->isReadonly();
if (vcstatus != NOLOCKING)
return isLocked();
else
return true;
}
string CVS::checkOut()
{
// to be sure we test it again...
if (!checkOutEnabled())
if (vcstatus != NOLOCKING && edit())
return string();
int ret = doVCCommand("cvs -q edit "
+ quoteName(onlyFileName(owner_->absFileName())),
FileName(owner_->filePath()));
if (ret)
FileName tmpf = FileName::tempName("lyxvcout");
if (tmpf.empty()) {
LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
return string();
ret = doVCCommand("cvs update "
+ quoteName(onlyFileName(owner_->absFileName())),
FileName(owner_->filePath()));
return ret ? string() : "CVS: Proceeded";
}
int rc = update(File, tmpf);
string log;
string const res = scanLogFile(tmpf, log);
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)));
tmpf.erase();
return rc ? string() : log.empty() ? "CVS: Proceeded" : "CVS: " + log;
}
bool CVS::checkOutEnabled()
{
return owner_->isReadonly();
if (vcstatus != NOLOCKING)
return !isLocked();
else
return true;
}
string CVS::repoUpdate()
{
lyxerr << "Sorry, not implemented." << endl;
return string();
FileName tmpf = FileName::tempName("lyxvcout");
if (tmpf.empty()) {
LYXERR(Debug::LYXVC, "Could not generate logfile " << tmpf);
return string();
}
getDiff(Directory, tmpf);
docstring res = tmpf.fileContents("UTF-8");
if (!res.empty()) {
LYXERR(Debug::LYXVC, "Diff detected:\n" << res);
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);
int ret = frontend::Alert::prompt(_("Changes detected"),
text, 0, 1, _("&Continue"), _("&Abort"), _("View &Log ..."));
if (ret == 2 ) {
dispatch(FuncRequest(LFUN_DIALOG_SHOW, "file " + tmpf.absFileName()));
ret = frontend::Alert::prompt(_("Changes detected"),
text, 0, 1, _("&Continue"), _("&Abort"));
hideDialogs("file", 0);
}
if (ret == 1 ) {
tmpf.removeFile();
return string();
}
}
int rc = update(Directory, tmpf);
res += "Update log:\n" + tmpf.fileContents("UTF-8");
tmpf.removeFile();
LYXERR(Debug::LYXVC, res);
return rc ? string() : "CVS: Proceeded" ;
}
bool CVS::repoUpdateEnabled()
{
return false;
return true;
}
@ -502,16 +709,33 @@ void CVS::revert()
{
// Reverts to the version in CVS repository and
// gets the updated version from the repository.
string const fil = quoteName(onlyFileName(owner_->absFileName()));
// This is sensitive operation, so at lest some check about
// existence of cvs program and its file
if (doVCCommand("cvs log "+ fil, FileName(owner_->filePath())))
return;
FileName f(owner_->absFileName());
f.removeFile();
doVCCommand("cvs -q update " + fil,
FileName(owner_->filePath()));
owner_->markClean();
CvsStatus status = getStatus();
switch (status) {
case UpToDate:
if (vcstatus != NOLOCKING)
unedit();
break;
case NeedsMerge:
case NeedsCheckout:
case LocallyModified: {
FileName f(owner_->absFileName());
f.removeFile();
update(File, FileName());
owner_->markClean();
break;
}
case LocallyAdded:
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:
frontend::Alert::error(_("Revision control error."),
bformat(_("Bad status when checking in changes.\n"
"\n'%1$s'\n\n"),
toString(status)));
break;
}
}
@ -532,7 +756,7 @@ bool CVS::undoLastEnabled()
void CVS::getLog(FileName const & tmpf)
{
doVCCommand("cvs log " + quoteName(onlyFileName(owner_->absFileName()))
doVCCommand("cvs log " + getTarget(File)
+ " > " + quoteName(tmpf.toFilesystemEncoding()),
FileName(owner_->filePath()));
}

View File

@ -88,7 +88,7 @@ protected:
virtual void scanMaster() = 0;
// GUI container for doVCCommandCall
int doVCCommand(std::string const & cmd, support::FileName const & path);
int doVCCommand(std::string const & cmd, support::FileName const & path, bool reportError = true);
/**
* doVCCommandCall - call out to the version control utility
* @param cmd the command to execute
@ -210,6 +210,10 @@ public:
virtual void getLog(support::FileName const &);
/// Check for messages in cvs output.
/// Returns conflict line.
std::string scanLogFile(support::FileName const & f, std::string & status);
virtual std::string const versionString() const {
return "CVS: " + version_;
}
@ -224,13 +228,46 @@ public:
protected:
virtual void scanMaster();
/// the mode of operation for some VC commands
enum OperationMode {
Directory = 0,
File = 1
};
/// possible status values of file
enum CvsStatus {
UpToDate = 0,
LocallyModified = 1,
LocallyAdded = 2,
NeedsMerge = 3,
NeedsCheckout = 4,
NoCvsFile = 5,
StatusError = 6
};
private:
support::FileName file_;
// revision number from scanMaster
std::string version_;
/// The user currently keeping the lock on the VC file.
std::string locker_;
/// return the quoted pathname if Directory or filename if File
virtual std::string const getTarget(OperationMode opmode) const;
/// collect the diff of file or directory against repository
/// result is placed in temporary file
void getDiff(OperationMode opmode, support::FileName const & tmpf);
/// make the file ready for editing:
/// save a copy in CVS/Base and change file permissions to rw if needed
virtual int edit();
/// revert the edit operation
virtual int unedit();
/// retrieve repository changes into working copy
virtual int update(OperationMode opmode, support::FileName const & tmpf);
/// check readonly state for file
/// assume true when file is writable
virtual bool isLocked() const;
/// query and parse the cvs status of file
virtual CvsStatus getStatus();
/// convert enum to string
virtual docstring toString(CvsStatus status) const;
};