Implement VCS copy and rename

The advantage of having this in LyX is the intelligent file name handling
of included files. Implementation as discussed on the list, but ensure also
that an attempt to use locked files fails.
This commit is contained in:
Georg Baum 2013-02-05 21:31:58 +01:00
parent 067fbe49cd
commit 0526eb9d47
11 changed files with 952 additions and 30 deletions

View File

@ -19147,6 +19147,109 @@ co -f -u<version> <file-name>
\begin_layout Subsubsection \begin_layout Subsubsection
\family sans
Copy
\end_layout
\begin_layout Standard
This will create a copy of the current document.
Since RCS does not support copy operations natively, the version history
is not preserved, and the copy is added as a new file.
It requires a clean document without any changes since the last checkin.
You are asked for a file name and a description of the copy operation.
After that the copy is created, both locally and in the repository.
If the parent directories of the copied and original document differ, all
relative paths of included files of the copy are adjusted (like in
\family sans
File\SpecialChar \menuseparator
Save As
\family default
\SpecialChar \ldots{}
).
Finally, the copy is loaded instead of the original document.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
RCS
\begin_inset space ~
\end_inset
commands:
\begin_inset space ~
\end_inset
\begin_inset Newline newline
\end_inset
Copy
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
ci -q -u
\family typewriter
-i
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection
\family sans \family sans
Undo Last Checkin Undo Last Checkin
\end_layout \end_layout
@ -19551,6 +19654,237 @@ CVS command:
cvs -q unedit "<file-name>" cvs -q unedit "<file-name>"
\end_layout \end_layout
\begin_layout Subsubsection
\family sans
Copy
\end_layout
\begin_layout Standard
This will create a copy of the current document.
Since CVS does not support copy operations natively, the version history
is not preserved, and the copy is added as a new file.
It requires a clean document without any changes since the last checkin.
You are asked for a file name and a description of the copy operation.
After that the copy is created, both locally and in the repository.
If the parent directories of the copied and original document differ, all
relative paths of included files of the copy are adjusted (like in
\family sans
File\SpecialChar \menuseparator
Save As
\family default
\SpecialChar \ldots{}
).
Finally, the copy is loaded instead of the original document.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
CVS
\begin_inset space ~
\end_inset
commands:
\begin_inset space ~
\end_inset
\begin_inset Newline newline
\end_inset
Copy
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
cvs -q add
\family typewriter
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection
\family sans
Rename
\end_layout
\begin_layout Standard
This will rename the current document.
Since CVS does not support rename operations natively, the version history
is not preserved, the renamed document is added as a new file, and the
original document is deleted.
It requires a clean document without any changes since the last checkin.
You are asked for a file name and a description of the rename operation.
After that the document is renamed, both locally and in the repository.
If the parent directories of the new and old file names differ, all relative
paths of included files are adjusted (like in
\family sans
File\SpecialChar \menuseparator
Save As
\family default
\SpecialChar \ldots{}
).
Finally, the document is reloaded using the new name.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
CVS
\begin_inset space ~
\end_inset
commands:
\begin_inset space ~
\end_inset
\begin_inset Newline newline
\end_inset
Rename
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
to
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
\family typewriter
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
cvs -q add
\family typewriter
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
cvs -q remove
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection \begin_layout Subsubsection
Update of the local directory checkout from repository Update of the local directory checkout from repository
\end_layout \end_layout
@ -19926,6 +20260,168 @@ svn revert -q
\end_inset \end_inset
\end_layout
\begin_layout Subsubsection
\family sans
Copy
\end_layout
\begin_layout Standard
This will create a copy of the current document including the version history.
It requires a clean document without any changes since the last checkin.
You are asked for a file name and a description of the copy operation.
After that the copy is created, both locally and in the repository.
If the parent directories of the copied and original document differ, all
relative paths of included files of the copy are adjusted (like in
\family sans
File\SpecialChar \menuseparator
Save As
\family default
\SpecialChar \ldots{}
).
Finally, the copy is loaded instead of the original document.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
SVN
\begin_inset space ~
\end_inset
commands:
\begin_inset space ~
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
svn copy -q
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
svn commit
\end_layout
\end_inset
\end_layout
\begin_layout Subsubsection
\family sans
Rename
\end_layout
\begin_layout Standard
This will rename the current document including the version history.
It requires a clean document without any changes since the last checkin.
You are asked for a file name and a description of the rename operation.
After that the document is renamed, both locally and in the repository.
If the parent directories of the new and old file names differ, all relative
paths of included files are adjusted (like in
\family sans
File\SpecialChar \menuseparator
Save As
\family default
\SpecialChar \ldots{}
).
Finally, the document is reloaded using the new name.
\end_layout
\begin_layout Labeling
\labelwidthstring 00.00.0000
SVN
\begin_inset space ~
\end_inset
commands:
\begin_inset space ~
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
svn move -q
\family typewriter
\begin_inset Quotes eld
\end_inset
<file-name>
\begin_inset Quotes erd
\end_inset
\begin_inset Quotes eld
\end_inset
<new-file-name>
\begin_inset Quotes erd
\end_inset
\end_layout
\end_inset
\begin_inset Newline newline
\end_inset
\begin_inset Flex Code
status collapsed
\begin_layout Plain Layout
svn commit
\end_layout
\end_inset
\end_layout \end_layout
\begin_layout Subsubsection \begin_layout Subsubsection

View File

@ -83,7 +83,7 @@
\begin_body \begin_body
\begin_layout Section* \begin_layout Section*
LFUNs documentation automatically generated 2013-02-03 LFUNs documentation automatically generated 2013-02-05
\end_layout \end_layout
\begin_layout Standard \begin_layout Standard
@ -4142,6 +4142,68 @@ Syntax vc-register
Origin Lgb, 1 Jul 1997 Origin Lgb, 1 Jul 1997
\end_layout \end_layout
\begin_layout Subsection*
LFUN_VC_RENAME
\end_layout
\begin_layout Description
Action Renames the document to another name.
\end_layout
\begin_layout Description
Notion Renaming with revision history is only supported by SVN.
For CVS it is simulated by adding the document under a new name and deleting
the old one.
For RCS it is not supported.
Disabled if uncommitted changes exist.
\end_layout
\begin_layout Description
Syntax vc-rename <FILENAME>
\end_layout
\begin_layout Description
Params <FILENAME>: New name of the document.
\begin_inset Newline newline
\end_inset
A file dialog is opened if no filename is given.
\end_layout
\begin_layout Description
Origin gb, 05 Feb 2013
\end_layout
\begin_layout Subsection*
LFUN_VC_COPY
\end_layout
\begin_layout Description
Action Copies the document to another name.
\end_layout
\begin_layout Description
Notion Copying with revision history is only supported by SVN.
For RCS and CVS it is simulated by adding the document under a new name.
Disabled if uncommitted changes exist.
\end_layout
\begin_layout Description
Syntax vc-copy <FILENAME>
\end_layout
\begin_layout Description
Params <FILENAME>: New name of the document.
\begin_inset Newline newline
\end_inset
A file dialog is opened if no filename is given.
\end_layout
\begin_layout Description
Origin gb, 05 Feb 2013
\end_layout
\begin_layout Subsection* \begin_layout Subsection*
LFUN_VC_CHECK_IN LFUN_VC_CHECK_IN
\end_layout \end_layout

View File

@ -74,6 +74,8 @@ Menuset
OptItem "Register...|R" "vc-register" OptItem "Register...|R" "vc-register"
OptItem "Check In Changes...|I" "vc-check-in" OptItem "Check In Changes...|I" "vc-check-in"
OptItem "Check Out for Edit|O" "vc-check-out" OptItem "Check Out for Edit|O" "vc-check-out"
OptItem "Copy|p" "vc-copy"
OptItem "Rename|R" "vc-rename"
OptItem "Update Local Directory From Repository|d" "vc-repo-update" OptItem "Update Local Directory From Repository|d" "vc-repo-update"
OptItem "Revert to Repository Version|v" "vc-revert" OptItem "Revert to Repository Version|v" "vc-revert"
OptItem "Undo Last Check In|U" "vc-undo-last" OptItem "Undo Last Check In|U" "vc-undo-last"

View File

@ -453,6 +453,9 @@ enum FuncCode
LFUN_BRANCH_MASTER_ACTIVATE, // spitz 20120930 LFUN_BRANCH_MASTER_ACTIVATE, // spitz 20120930
LFUN_BRANCH_MASTER_DEACTIVATE, // spitz 20120930 LFUN_BRANCH_MASTER_DEACTIVATE, // spitz 20120930
LFUN_ENVIRONMENT_SPLIT, // spitz 20121223 LFUN_ENVIRONMENT_SPLIT, // spitz 20121223
LFUN_VC_RENAME, // gb 20130205
LFUN_VC_COPY, // gb 20130205
// 355
LFUN_LASTACTION // end of the table LFUN_LASTACTION // end of the table
}; };

View File

@ -2146,6 +2146,34 @@ void LyXAction::init()
* \endvar * \endvar
*/ */
{ LFUN_VC_REGISTER, "vc-register", ReadOnly, System }, { LFUN_VC_REGISTER, "vc-register", ReadOnly, System },
/*!
* \var lyx::FuncCode lyx::LFUN_VC_RENAME
* \li Action: Renames the document to another name.
* \li Notion: Renaming with revision history is only supported by SVN.
For CVS it is simulated by adding the document under a new
name and deleting the old one. For RCS it is not supported.
Disabled if uncommitted changes exist.
* \li Syntax: vc-rename <FILENAME>
* \li Params: <FILENAME>: New name of the document.\n
* A file dialog is opened if no filename is given.
* \li Origin: gb, 05 Feb 2013
* \endvar
*/
{ LFUN_VC_RENAME, "vc-rename", ReadOnly, System },
/*!
* \var lyx::FuncCode lyx::LFUN_VC_COPY
* \li Action: Copies the document to another name.
* \li Notion: Copying with revision history is only supported by SVN.
For RCS and CVS it is simulated by adding the document
under a new name.
Disabled if uncommitted changes exist.
* \li Syntax: vc-copy <FILENAME>
* \li Params: <FILENAME>: New name of the document.\n
* A file dialog is opened if no filename is given.
* \li Origin: gb, 05 Feb 2013
* \endvar
*/
{ LFUN_VC_COPY, "vc-copy", ReadOnly, System },
/*! /*!
* \var lyx::FuncCode lyx::LFUN_VC_CHECK_IN * \var lyx::FuncCode lyx::LFUN_VC_CHECK_IN
* \li Action: Checks-in/commits the changes of the registered file to the repository. * \li Action: Checks-in/commits the changes of the registered file to the repository.

View File

@ -173,6 +173,44 @@ bool LyXVC::registrer()
} }
string LyXVC::rename(FileName const & fn)
{
LYXERR(Debug::LYXVC, "LyXVC: rename");
if (!vcs || fileInVC(fn))
return string();
docstring response;
bool ok = Alert::askForText(response, _("LyX VC: Log message"),
_("(no log message)"));
if (!ok) {
LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
return string();
}
if (response.empty())
response = _("(no log message)");
string ret = vcs->rename(fn, to_utf8(response));
return ret;
}
string LyXVC::copy(FileName const & fn)
{
LYXERR(Debug::LYXVC, "LyXVC: copy");
if (!vcs || fileInVC(fn))
return string();
docstring response;
bool ok = Alert::askForText(response, _("LyX VC: Log message"),
_("(no log message)"));
if (!ok) {
LYXERR(Debug::LYXVC, "LyXVC: user cancelled");
return string();
}
if (response.empty())
response = _("(no log message)");
string ret = vcs->copy(fn, to_utf8(response));
return ret;
}
LyXVC::CommandResult LyXVC::checkIn(string & log) LyXVC::CommandResult LyXVC::checkIn(string & log)
{ {
LYXERR(Debug::LYXVC, "LyXVC: checkIn"); LYXERR(Debug::LYXVC, "LyXVC: checkIn");
@ -273,6 +311,7 @@ string LyXVC::toggleReadOnly()
return log; return log;
} }
case VCS::NOLOCKING: case VCS::NOLOCKING:
case VCS::UNVERSIONED:
break; break;
} }
return string(); return string();
@ -282,7 +321,7 @@ string LyXVC::toggleReadOnly()
bool LyXVC::inUse() const bool LyXVC::inUse() const
{ {
if (vcs) if (vcs)
return true; return vcs->status() != VCS::UNVERSIONED;
return false; return false;
} }
@ -328,6 +367,20 @@ string LyXVC::revisionInfo(RevisionInfo const info) const
} }
bool LyXVC::renameEnabled() const
{
if (!inUse())
return false;
return vcs->renameEnabled();
}
bool LyXVC::copyEnabled() const
{
return inUse();
}
bool LyXVC::checkOutEnabled() const bool LyXVC::checkOutEnabled() const
{ {
return vcs && vcs->checkOutEnabled(); return vcs && vcs->checkOutEnabled();
@ -340,6 +393,12 @@ bool LyXVC::checkInEnabled() const
} }
bool LyXVC::isCheckInWithConfirmation() const
{
return vcs && vcs->isCheckInWithConfirmation();
}
bool LyXVC::lockingToggleEnabled() const bool LyXVC::lockingToggleEnabled() const
{ {
return vcs && vcs->lockingToggleEnabled(); return vcs && vcs->lockingToggleEnabled();

View File

@ -84,11 +84,22 @@ public:
// by the next multiple messages on the top of the processed dispatch // by the next multiple messages on the top of the processed dispatch
// machinery. // machinery.
///
std::string rename(support::FileName const &);
/// Does the current VC support this operation?
bool renameEnabled() const;
///
std::string copy(support::FileName const &);
/// Does the current VC support this operation?
bool copyEnabled() const;
/// Unlock and commit changes. /// Unlock and commit changes.
/// \p log is non-empty on success and may be empty on failure. /// \p log is non-empty on success and may be empty on failure.
CommandResult checkIn(std::string & log); CommandResult checkIn(std::string & log);
/// Does the current VC support this operation? /// Does the current VC support this operation?
bool checkInEnabled() const; bool checkInEnabled() const;
/// Should a log message be provided for next checkin?
bool isCheckInWithConfirmation() const;
/// Lock/update and prepare to edit document. Returns log. /// Lock/update and prepare to edit document. Returns log.
std::string checkOut(); std::string checkOut();
@ -146,6 +157,7 @@ public:
std::string toggleReadOnly(); std::string toggleReadOnly();
/// Is the document under administration by VCS? /// Is the document under administration by VCS?
/// returns false for unregistered documents in a path managed by VCS
bool inUse() const; bool inUse() const;
/// Returns the RCS + version number for messages /// Returns the RCS + version number for messages

View File

@ -233,6 +233,35 @@ void RCS::registrer(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();
}
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) LyXVC::CommandResult RCS::checkIn(string const & msg, string & log)
{ {
int ret = doVCCommand("ci -q -u -m\"" + msg + "\" " int ret = doVCCommand("ci -q -u -m\"" + msg + "\" "
@ -369,7 +398,7 @@ bool RCS::toggleReadOnlyEnabled()
// This got broken somewhere along lfuns dispatch reorganization. // This got broken somewhere along lfuns dispatch reorganization.
// reloadBuffer would be needed after this, but thats problematic // reloadBuffer would be needed after this, but thats problematic
// since we are inside Buffer::dispatch. // since we are inside Buffer::dispatch.
// return true; // return return status() != UNVERSIONED;
return false; return false;
} }
@ -518,6 +547,7 @@ void CVS::scanMaster()
LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\''); LYXERR(Debug::LYXVC, "\tlooking for `" << tmpf << '\'');
string line; string line;
static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)"); static regex const reg("/(.*)/(.*)/(.*)/(.*)/(.*)");
vcstatus = UNVERSIONED;
while (getline(ifs, line)) { while (getline(ifs, line)) {
LYXERR(Debug::LYXVC, "\t line: " << line); LYXERR(Debug::LYXVC, "\t line: " << line);
if (contains(line, tmpf)) { if (contains(line, tmpf)) {
@ -711,6 +741,39 @@ 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;
}
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) void CVS::getDiff(OperationMode opmode, FileName const & tmpf)
{ {
doVCCommandWithOutput("cvs diff " + getTarget(opmode), doVCCommandWithOutput("cvs diff " + getTarget(opmode),
@ -1097,14 +1160,18 @@ FileName const SVN::findFile(FileName const & file)
void SVN::scanMaster() void SVN::scanMaster()
{ {
// vcstatus code is somewhat superflous, until we want // vcstatus code other than UNVERSIONED is somewhat superflous,
// to implement read-only toggle for svn. // until we want to implement read-only toggle for svn.
vcstatus = NOLOCKING; FileName f = findFile(owner_->fileName());
if (checkLockMode()) { if (f.empty()) {
if (isLocked()) { vcstatus = UNVERSIONED;
vcstatus = LOCKED; } else {
} else { vcstatus = NOLOCKING;
vcstatus = UNLOCKED; if (checkLockMode()) {
if (isLocked())
vcstatus = LOCKED;
else
vcstatus = UNLOCKED;
} }
} }
} }
@ -1166,7 +1233,75 @@ void SVN::registrer(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<support::FileName> f;
f.push_back(owner_->fileName());
f.push_back(newFile);
string log;
if (checkIn(f, msg, log) != LyXVC::Success) {
cmd = "svn revert -q " +
quoteName(onlyFileName(owner_->absFileName())) + ' ' +
quoteName(relFile);
doVCCommand(cmd, path);
if (newFile.exists())
newFile.removeFile();
return string();
}
return log;
}
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<support::FileName> f(1, newFile);
string log;
if (checkIn(f, msg, log) == LyXVC::Success)
return log;
return string();
}
LyXVC::CommandResult SVN::checkIn(string const & msg, string & log) LyXVC::CommandResult SVN::checkIn(string const & msg, string & log)
{
vector<support::FileName> f(1, owner_->fileName());
return checkIn(f, msg, log);
}
LyXVC::CommandResult
SVN::checkIn(vector<support::FileName> const & f, string const & msg, string & log)
{ {
FileName tmpf = FileName::tempName("lyxvcout"); FileName tmpf = FileName::tempName("lyxvcout");
if (tmpf.empty()){ if (tmpf.empty()){
@ -1177,7 +1312,8 @@ LyXVC::CommandResult SVN::checkIn(string const & msg, string & log)
ostringstream os; ostringstream os;
os << "svn commit -m \"" << msg << '"'; os << "svn commit -m \"" << msg << '"';
os << ' ' << quoteName(onlyFileName(owner_->absFileName())); for (size_t i = 0; i < f.size(); ++i)
os << ' ' << quoteName(f[i].onlyFileName());
os << " > " << quoteName(tmpf.toFilesystemEncoding()); os << " > " << quoteName(tmpf.toFilesystemEncoding());
LyXVC::CommandResult ret = LyXVC::CommandResult ret =
doVCCommand(os.str(), FileName(owner_->filePath())) ? doVCCommand(os.str(), FileName(owner_->filePath())) ?

View File

@ -16,6 +16,7 @@
#include "support/FileName.h" #include "support/FileName.h"
#include <string> #include <string>
#include <vector>
#include "LyXVC.h" #include "LyXVC.h"
@ -31,7 +32,10 @@ public:
enum VCStatus { enum VCStatus {
UNLOCKED, UNLOCKED,
LOCKED, LOCKED,
NOLOCKING NOLOCKING,
/// This file is not in version control, but it could be aded
/// (because the path is under version control)
UNVERSIONED,
}; };
VCS(Buffer * b) : owner_(b) {} VCS(Buffer * b) : owner_(b) {}
@ -39,6 +43,12 @@ public:
/// register a file for version control /// register a file for version control
virtual void registrer(std::string const & msg) = 0; virtual void registrer(std::string const & msg) = 0;
/// can this operation be processed in the current VCS?
virtual bool renameEnabled() = 0;
/// rename a file. Return non-empty log on success, empty log on failure.
virtual std::string rename(support::FileName const &, std::string const &) = 0;
/// copy a file. Return non-empty log on success, empty log on failure.
virtual std::string copy(support::FileName const &, std::string const &) = 0;
/// check in the current revision. /// check in the current revision.
/// \p log is non-empty on success and may be empty on failure. /// \p log is non-empty on success and may be empty on failure.
virtual LyXVC::CommandResult virtual LyXVC::CommandResult
@ -142,6 +152,12 @@ public:
virtual void registrer(std::string const & msg); virtual void registrer(std::string const & msg);
virtual bool renameEnabled();
virtual std::string rename(support::FileName const &, std::string const &);
virtual std::string copy(support::FileName const &, std::string const &);
virtual LyXVC::CommandResult virtual LyXVC::CommandResult
checkIn(std::string const & msg, std::string & log); checkIn(std::string const & msg, std::string & log);
@ -217,6 +233,12 @@ public:
virtual void registrer(std::string const & msg); virtual void registrer(std::string const & msg);
virtual bool renameEnabled();
virtual std::string rename(support::FileName const &, std::string const &);
virtual std::string copy(support::FileName const &, std::string const &);
virtual LyXVC::CommandResult virtual LyXVC::CommandResult
checkIn(std::string const & msg, std::string & log); checkIn(std::string const & msg, std::string & log);
@ -347,6 +369,12 @@ public:
virtual void registrer(std::string const & msg); virtual void registrer(std::string const & msg);
virtual bool renameEnabled();
virtual std::string rename(support::FileName const &, std::string const &);
virtual std::string copy(support::FileName const &, std::string const &);
virtual LyXVC::CommandResult virtual LyXVC::CommandResult
checkIn(std::string const & msg, std::string & log); checkIn(std::string const & msg, std::string & log);
@ -398,6 +426,9 @@ protected:
bool isLocked() const; bool isLocked() const;
/// acquire/release write lock for the current file /// acquire/release write lock for the current file
bool fileLock(bool lock, support::FileName const & tmpf, std::string & status); bool fileLock(bool lock, support::FileName const & tmpf, std::string & status);
/// Check in files \p f with log \p msg
LyXVC::CommandResult checkIn(std::vector<support::FileName> const & f,
std::string const & msg, std::string & log);
private: private:
/// is the loaded file under locking policy? /// is the loaded file under locking policy?

View File

@ -1804,6 +1804,12 @@ bool GuiView::getStatus(FuncRequest const & cmd, FuncStatus & flag)
case LFUN_VC_REGISTER: case LFUN_VC_REGISTER:
enable = doc_buffer && !doc_buffer->lyxvc().inUse(); enable = doc_buffer && !doc_buffer->lyxvc().inUse();
break; break;
case LFUN_VC_RENAME:
enable = doc_buffer && doc_buffer->lyxvc().renameEnabled();
break;
case LFUN_VC_COPY:
enable = doc_buffer && doc_buffer->lyxvc().copyEnabled();
break;
case LFUN_VC_CHECK_IN: case LFUN_VC_CHECK_IN:
enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled(); enable = doc_buffer && doc_buffer->lyxvc().checkInEnabled();
break; break;
@ -2209,7 +2215,7 @@ void GuiView::insertLyXFile(docstring const & fname)
} }
bool GuiView::renameBuffer(Buffer & b, docstring const & newname) bool GuiView::renameBuffer(Buffer & b, docstring const & newname, RenameKind kind)
{ {
FileName fname = b.fileName(); FileName fname = b.fileName();
FileName const oldname = fname; FileName const oldname = fname;
@ -2263,27 +2269,74 @@ bool GuiView::renameBuffer(Buffer & b, docstring const & newname)
int const ret = Alert::prompt(_("Chosen File Already Open"), int const ret = Alert::prompt(_("Chosen File Already Open"),
text, 0, 1, _("&Rename"), _("&Cancel")); text, 0, 1, _("&Rename"), _("&Cancel"));
switch (ret) { switch (ret) {
case 0: return renameBuffer(b, docstring()); case 0: return renameBuffer(b, docstring(), kind);
case 1: return false; case 1: return false;
} }
//return false; //return false;
} }
if (FileName(fname).exists()) { bool const existsLocal = fname.exists();
bool const existsInVC = LyXVC::fileInVC(fname);
if (existsLocal || existsInVC) {
docstring const file = makeDisplayPath(fname.absFileName(), 30); docstring const file = makeDisplayPath(fname.absFileName(), 30);
docstring const text = bformat(_("The document %1$s already " if (kind != LV_WRITE_AS && existsInVC) {
"exists.\n\nDo you want to " // renaming to a name that is already in VC
"overwrite that document?"), // would not work
file); docstring text = bformat(_("The document %1$s "
int const ret = Alert::prompt(_("Overwrite document?"), "is already registered.\n\n"
text, 0, 2, _("&Overwrite"), _("&Rename"), _("&Cancel")); "Do you want to choose a new name?"),
switch (ret) { file);
case 0: break; docstring const title = (kind == LV_VC_RENAME) ?
case 1: return renameBuffer(b, docstring()); _("Rename document?") : _("Copy document?");
case 2: return false; docstring const button = (kind == LV_VC_RENAME) ?
_("&Rename") : _("&Copy");
int const ret = Alert::prompt(title, text, 0, 1,
button, _("&Cancel"));
switch (ret) {
case 0: return renameBuffer(b, docstring(), kind);
case 1: return false;
}
}
if (existsLocal) {
docstring text = bformat(_("The document %1$s "
"already exists.\n\n"
"Do you want to overwrite that document?"),
file);
int const ret = Alert::prompt(_("Overwrite document?"),
text, 0, 2, _("&Overwrite"),
_("&Rename"), _("&Cancel"));
switch (ret) {
case 0: break;
case 1: return renameBuffer(b, docstring(), kind);
case 2: return false;
}
} }
} }
switch (kind) {
case LV_VC_RENAME: {
string msg = b.lyxvc().rename(fname);
if (msg.empty())
return false;
message(from_utf8(msg));
break;
}
case LV_VC_COPY: {
string msg = b.lyxvc().copy(fname);
if (msg.empty())
return false;
message(from_utf8(msg));
break;
}
case LV_WRITE_AS:
break;
}
// LyXVC created the file already in case of LV_VC_RENAME or
// LV_VC_COPY, but call saveBuffer() nevertheless to get
// relative paths of included stuff right if we moved e.g. from
// /a/b.lyx to /a/c/b.lyx.
bool const saved = saveBuffer(b, fname); bool const saved = saveBuffer(b, fname);
if (saved) if (saved)
b.reload(false); b.reload(false);
@ -2412,7 +2465,7 @@ bool GuiView::saveBuffer(Buffer & b, FileName const & fn)
return false; return false;
} }
return saveBuffer(b); return saveBuffer(b, fn);
} }
@ -2809,6 +2862,36 @@ void GuiView::dispatchVC(FuncRequest const & cmd, DispatchResult & dr)
} }
break; break;
case LFUN_VC_RENAME:
case LFUN_VC_COPY: {
if (!buffer || !ensureBufferClean(buffer))
break;
if (buffer->lyxvc().inUse() && !buffer->isReadonly()) {
if (buffer->lyxvc().isCheckInWithConfirmation()) {
// Some changes are not yet committed.
// We test here and not in getStatus(), since
// this test is expensive.
string log;
LyXVC::CommandResult ret =
buffer->lyxvc().checkIn(log);
dr.setMessage(log);
if (ret == LyXVC::ErrorCommand ||
ret == LyXVC::Success)
reloadBuffer(*buffer);
if (buffer->lyxvc().isCheckInWithConfirmation()) {
frontend::Alert::error(
_("Revision control error."),
_("Document could not be checked in."));
break;
}
}
RenameKind const kind = (cmd.action() == LFUN_VC_RENAME) ?
LV_VC_RENAME : LV_VC_COPY;
renameBuffer(*buffer, cmd.argument(), kind);
}
break;
}
case LFUN_VC_CHECK_IN: case LFUN_VC_CHECK_IN:
if (!buffer || !ensureBufferClean(buffer)) if (!buffer || !ensureBufferClean(buffer))
break; break;
@ -3606,6 +3689,8 @@ void GuiView::dispatch(FuncRequest const & cmd, DispatchResult & dr)
break; break;
case LFUN_VC_REGISTER: case LFUN_VC_REGISTER:
case LFUN_VC_RENAME:
case LFUN_VC_COPY:
case LFUN_VC_CHECK_IN: case LFUN_VC_CHECK_IN:
case LFUN_VC_CHECK_OUT: case LFUN_VC_CHECK_OUT:
case LFUN_VC_REPO_UPDATE: case LFUN_VC_REPO_UPDATE:

View File

@ -355,6 +355,8 @@ private:
/// ///
bool exportBufferAs(Buffer & b); bool exportBufferAs(Buffer & b);
///
enum RenameKind { LV_WRITE_AS, LV_VC_RENAME, LV_VC_COPY };
/// Save a buffer as a new file. /// Save a buffer as a new file.
/** /**
Write a buffer to a new file name and rename the buffer Write a buffer to a new file name and rename the buffer
@ -369,8 +371,14 @@ private:
If 'newname' is non-empty and has an absolute path, that is used. If 'newname' is non-empty and has an absolute path, that is used.
Otherwise the base directory of the buffer is used as the base Otherwise the base directory of the buffer is used as the base
for any relative path in 'newname'. for any relative path in 'newname'.
*/
bool renameBuffer(Buffer & b, docstring const & newname); \p kind controls what is done besides the pure renaming:
* LV_WRITE_AS => The buffer is written without version control actions.
* LV_VC_RENAME => The file is renamed in version control.
* LV_VC_COPY => The file is copied in version control.
*/
bool renameBuffer(Buffer & b, docstring const & newname,
RenameKind kind = LV_WRITE_AS);
/// ///
bool saveBuffer(Buffer & b); bool saveBuffer(Buffer & b);
/// save and rename buffer to fn. If fn is empty, the buffer /// save and rename buffer to fn. If fn is empty, the buffer