SVN Library -- first release + test app

git-svn-id: svn://ultimatepp.org/upp/trunk@299 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
mdelfede 2008-06-15 00:53:12 +00:00
parent 2bf2eade17
commit ad3ced97b7
7 changed files with 1492 additions and 0 deletions

869
bazaar/SvnLib/SvnLib.cpp Normal file
View file

@ -0,0 +1,869 @@
#include <SysExec/SysExec.h>
#include "SvnLib.h"
NAMESPACE_UPP
///////////////////////////////////////////////////////////////////////////////////////////////
// Human-readable error messages
const char *Svn::ErrorMessages[] =
{
"Svn : Ok",
"Svn : Unknown Error",
"Svn : Svn application not found",
"Svn : Not a SVN repository",
"Svn : Not connected to a repository",
"Svn : File not found",
"Svn : Path not found",
"Svn : File exists",
"Svn : Directory exists",
"Svn : Path exists",
"Svn : Directory is not empty",
"Svn : Path is not a directory",
"Svn : Can't remove directory. Read only?",
"Svn : Can't write do directory. Read only?"
"Svn : Path is not in active repository",
"Svn : Invalid repository URL",
"Svn : Bad XML found",
"Svn : Bad login. Check username/password"
} ; // END Svn::ErrorMessages[]
///////////////////////////////////////////////////////////////////////////////////////////////
// Human-readable status strings
const char *Svn::StatusStrings[] =
{
"unknown",
"added",
"missing",
"incomplete",
"replaced",
"modified",
"merged",
"conflicted",
"obstructed",
"ignored",
"external",
"unversioned"
}; // END Svn::StatusStrings
const int Svn::NumStatus = sizeof(StatusStrings) / sizeof(char *);
///////////////////////////////////////////////////////////////////////////////////////////////
// from status string, gets status
Svn::ItemStatus Svn::String2Status(String const &s)
{
for(int i = 0; i < NumStatus; i++)
if(s == StatusStrings[i])
return (ItemStatus)i;
return unknown;
} // END Svn::String2Status()
///////////////////////////////////////////////////////////////////////////////////////////////
// from status, gets human-readable string
String Svn::Status2String(ItemStatus s)
{
if(s >= unknown && s <= unversioned)
return StatusStrings[s];
else
return "BAD STATUS";
} // END Svn::Status2String()
///////////////////////////////////////////////////////////////////////////////////////////////
// executes svn, passing cmdline as argument, gathering output in Output
// in xml format (where available) if xml is true
Svn::Errors Svn::ExecSvn(String const &SvnArgs, String &OutStr, String &ErrStr, bool xml)
{
// adjust args if we wants xml output
String args = (xml ? "--xml " : "") + SvnArgs;
// adjust environment to get output in en_IN locale
// otherwise it becomes difficult to understand command responses....
VectorMap<String, String> env;
env.Add("LC_MESSAGES", "en_IN");
// executes svn command
bool result = SysExec("svn", args, env, OutStr, ErrStr);
FLastCommandOutput = OutStr;
FLastCommandError = ErrStr;
// if all ok, returns ok
if(result && ErrStr == "")
return SetError(Svn::Ok);
// catches spawning errors
if(ErrStr.Find("Couldn't fork") >=0 || ErrStr.Find("Error spawning process") >=0)
return SetError(NoSvnApp);
// catches svn command errors
// checks for ill-formed URLs
if(ErrStr.Find("does not appear to be a URL") >= 0)
return SetError(Svn::NotAnURL);
// ckecks if could write to folder
if(ErrStr.Find("Can't create directory") >= 0)
return SetError(Svn::CantWriteDirectory);
// checks wether we tried to operate on a non-svn folder
if (ErrStr.Find("is not a working copy") >= 0)
return SetError(Svn::NotARepo);
return SetError(Svn::UnknownError);
} // END Svn::ExecSvn()
///////////////////////////////////////////////////////////////////////////////////////////////
// builds username/password string
String Svn::BuildUserPassword(void)
{
if(FAnonymous)
return " ";
else
return " --username " + FUserName + " --password " + FPassword + " ";
} // END Svn::BuildUserPassword()
///////////////////////////////////////////////////////////////////////////////////////////////
// builds revision string
String Svn::BuildRevision(String const &Revision)
{
if(Revision != "")
return " --revision " + Revision + " ";
else
return " ";
} // END Svn::BuildRevision()
///////////////////////////////////////////////////////////////////////////////////////////////
// checks wether a string is a remote URL or a local path
bool Svn::IsUrl(String const &path)
{
if(
path.Find("http://") >= 0 ||
path.Find("https://") >= 0 ||
path.Find("svn://") >= 0 ||
path.Find("file://") >= 0
)
return true;
return false;
} // END Svn::IsUrl()
///////////////////////////////////////////////////////////////////////////////////////////////
// checks whether a path is inside local repository
bool Svn::IsLocalPath(String const &path)
{
return (path.Find(FLocalPath) == 0);
} // END Svn::IsLocalPath()
///////////////////////////////////////////////////////////////////////////////////////////////
// checks whether a path is inside remote repository
bool Svn::IsRemotePath(String const &path)
{
return (path.Find(FRepositoryRoot) == 0);
} // END Svn::IsRemotePath()
///////////////////////////////////////////////////////////////////////////////////////////////
// checks whether a path is inside repository, remote or local side
bool Svn::IsRepositoryPath(String const &path)
{
return IsLocalPath(path) || IsRemotePath(path);
} // END Svn::IsRepositoryPath()
///////////////////////////////////////////////////////////////////////////////////////////////
// initializer... used by constructor and on disconnection
void Svn::Init(void)
{
// log-in stuffs
FUserName = "";
FPassword = "";
FAnonymous = true;
// svn repository info stuffs
FLocalPath = "";
FRepositoryRoot = "";
FRepositoryUUID = "";
FCheckedRevisionAuthor = "";
FCheckedRevision = 0;
FCheckedRevisionDate = "";
FHeadRevisionAuthor = "";
FHeadRevision = 0;
FHeadRevisionDate = "";
FLastError = Ok;
FConnected = false;
FLastCommandOutput = "";
FLastCommandError = "";
} // END Svn::Init()
///////////////////////////////////////////////////////////////////////////////////////////////
// constructor class Svn
Svn::Svn()
{
Init();
} // END constructor class Svn
///////////////////////////////////////////////////////////////////////////////////////////////
// destructor class Svn
Svn::~Svn()
{
} // END destructor class Svn
///////////////////////////////////////////////////////////////////////////////////////////////
// checks wether a path is a svn
bool Svn::IsSvn(String const &LocalPath)
{
String Out, Err;
return( ExecSvn("info " + LocalPath, Out, Err) == Svn::Ok);
} // END Svn::IsSvn()
///////////////////////////////////////////////////////////////////////////////////////////////
// parses xml output of 'svn info' command
Svn::Errors Svn::ParseInfo(String const &str, EntryInfo &info)
{
try
{
XmlParser p(str);
while (!p.IsTag() &&!p.End())
p.Skip();
p.PassTag("info");
while (!p.End())
{
if (p.Tag("entry"))
{
info.kind = p["kind"];
info.revision = atoi(p["revision"]);
info.path = p["path"];
while (!p.End())
{
if (p.Tag("url"))
{
info.url = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("repository"))
{
while(!p.End())
{
if(p.Tag("root"))
{
info.repositoryRoot = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("uuid"))
{
info.repositoryUUID = p.ReadText();
p.SkipEnd();
}
else
p.Skip();
}
}
else if(p.Tag("commit"))
{
while(!p.End())
{
if(p.Tag("revision"))
{
info.commitRevision = atoi(p.ReadText());
p.SkipEnd();
}
else if(p.Tag("author"))
{
info.commitAuthor = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("date"))
{
info.commitDate = p.ReadText();
p.SkipEnd();
}
else
p.Skip();
}
}
else
p.Skip();
}
}
else
p.Skip();
}
return Ok;
}
catch (XmlError)
{
return BadXml;
}
} // END Svn::ParseInfo()
///////////////////////////////////////////////////////////////////////////////////////////////
// parses xml output of 'svn log' command
Svn::Errors Svn::ParseLog(String const &str, Array<LogInfo>&infoArray)
{
try
{
XmlParser p(str);
while (!p.IsTag() &&!p.End())
p.Skip();
p.PassTag("log");
while (!p.End())
{
if (p.Tag("logentry"))
{
LogInfo &info = infoArray.Add();
info.author = "";
info.date = "";
info.message = "";
info.revision = atoi(p["revision"]);
while (!p.End())
{
if (p.Tag("author"))
{
info.author = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("date"))
{
info.date = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("msg"))
{
info.message = p.ReadText();
p.SkipEnd();
}
else
p.Skip();
}
}
else
p.Skip();
}
return Ok;
}
catch (XmlError)
{
return BadXml;
}
} // END Svn::ParseLog()
///////////////////////////////////////////////////////////////////////////////////////////////
// parses xml output of 'svn status' command
Svn::Errors Svn::ParseStatus(String const &str, Array<StatusInfo> &infoArray)
{
try
{
XmlParser p(str);
while (!p.IsTag() &&!p.End())
p.Skip();
p.PassTag("status");
p.PassTag("target");
while (!p.End())
{
if (p.Tag("entry"))
{
StatusInfo &info = infoArray.Add();
info.path = "";
info.props = "";
info.status = unknown;
info.revision = -1;
info.commitRevision = -1;
info.commitAuthor = "";
info.commitDate = "";
info.path = p["path"];
while (!p.End())
{
if (p.Tag("wc-status"))
{
info.props = p["props"];
info.status = String2Status(p["item"]);
info.revision = atoi(p["revision"]);
p.SkipEnd();
}
else if(p.Tag("commit"))
{
info.commitRevision = atoi(p["revision"]);
p.SkipEnd();
}
else if(p.Tag("author"))
{
info.commitAuthor = p.ReadText();
p.SkipEnd();
}
else if(p.Tag("date"))
{
info.commitDate = p.ReadText();
p.SkipEnd();
}
else
p.Skip();
}
}
else
p.Skip();
}
return Ok;
}
catch (XmlError)
{
return BadXml;
}
} // END Svn::ParseStatus()
///////////////////////////////////////////////////////////////////////////////////////////////
// sets user authentication
void Svn::SetUser(String const &UserName, String const &Password)
{
FUserName = UserName;
FPassword = Password;
FAnonymous = false;
} // END Svn::SetUser()
///////////////////////////////////////////////////////////////////////////////////////////////
// sets user authentication
void Svn::SetUser(void)
{
FUserName = "";
FPassword = "";
FAnonymous = true;
} // END Svn::SetUser()
///////////////////////////////////////////////////////////////////////////////////////////////
// connects to the local copy
Svn::Errors Svn::Connect(String const &LocalPath)
{
Svn::Errors result;
String Out, Err;
// first, checks wether is a svn repo
if(!IsSvn(LocalPath))
return SetError(Svn::NotARepo);
// executes 'svn info' command -- gets current checked-in information
result = ExecSvn("info " + BuildUserPassword() + LocalPath, Out, Err, true);
if(result != Svn::Ok)
return SetError(result);
// parses xml info output
EntryInfo info;
result = ParseInfo(Out, info);
if(result != Svn::Ok)
return SetError(result);
// stores repository info
FLocalPath = LocalPath;
FRepositoryRoot = info.repositoryRoot;
FRepositoryUUID = info.repositoryUUID;
FCheckedRevisionAuthor = info.commitAuthor;
FCheckedRevision = info.revision;
FCheckedRevisionDate = info.commitDate;
// executes 'svn info -r HEAD' command -- gets HEAD information
result = ExecSvn("info -r HEAD " + BuildUserPassword() + LocalPath, Out, Err, true);
if(result != Svn::Ok)
return SetError(result);
// parses xml info output
result = ParseInfo(Out, info);
if(result != Svn::Ok)
return SetError(result);
// stores repository info
FHeadRevisionAuthor = info.commitAuthor;
FHeadRevision = info.revision;
FHeadRevisionDate = info.commitDate;
FConnected = true;
FLastError = Ok;
return SetError(Svn::Ok);
} // END Svn::Connect()
///////////////////////////////////////////////////////////////////////////////////////////////
// disconnects from local copy
Svn::Errors Svn::Disconnect(void)
{
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// reinitializes
Init();
return Svn::Ok;
} // END Svn::Disconnect()
///////////////////////////////////////////////////////////////////////////////////////////////
// checks out from server
Svn::Errors Svn::Checkout(String const &Url, String const &LocalPath, String const &Revision)
{
String Out, Err;
String Path;
Svn::Errors result;
// adjust final path separator
Path = LocalPath;
if(Path[Path.GetLength()-1] != DIR_SEP)
Path << DIR_SEP;
// fists, looks at localpath; if it does exist, it must be empty
if(Upp::DirectoryExists(Path))
{
// checks if directory is empty
Array<FileSystemInfo::FileInfo> fileInfo = StdFileSystemInfo().Find(Path + "*");
bool found = false;
for(int i = 0; i < fileInfo.GetCount(); i++)
{
if(fileInfo[i].filename != "." && fileInfo[i].filename != "..")
{
found = true;
break;
}
}
if(found)
return SetError(DirectoryNotEmpty);
// removes the path
if(!DirectoryDelete(Path))
return SetError(CantRemoveDirectory);
}
// well, now we can do the checkout
result = ExecSvn("checkout -q --no-auth-cache --non-interactive" + BuildUserPassword() + BuildRevision(Revision) + Url + " " + Path, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
// checkout should be done, connect to checked out folder
return SetError(Connect(Path));
} // END Svn::Checkout()
///////////////////////////////////////////////////////////////////////////////////////////////
// updates current copy from server
Svn::Errors Svn::Update(String const &Revision)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// well, now we can do the update
result = ExecSvn("update -q --no-auth-cache --non-interactive" + BuildUserPassword() + BuildRevision(Revision) + FLocalPath, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
// executes 'svn info' command -- gets current checked-in information
result = ExecSvn("info " + BuildUserPassword() + FLocalPath, Out, Err, true);
if(result != Svn::Ok)
return SetError(result);
// parses xml info output
EntryInfo info;
result = ParseInfo(Out, info);
if(result != Svn::Ok)
return SetError(result);
// stores repository info
FRepositoryRoot = info.repositoryRoot;
FRepositoryUUID = info.repositoryUUID;
FCheckedRevisionAuthor = info.commitAuthor;
FCheckedRevision = info.revision;
FCheckedRevisionDate = info.commitDate;
return SetError(Svn::Ok);
} // END Svn::Update()
///////////////////////////////////////////////////////////////////////////////////////////////
// commits to server
Svn::Errors Svn::Commit(String const &Message)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// well, now we can do the commit
result = ExecSvn("commit -q --no-auth-cache --non-interactive" + BuildUserPassword() + " -- message " + Message, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
return SetError(Svn::Ok);
} // END Svn::Commit()
///////////////////////////////////////////////////////////////////////////////////////////////
// adds file(s) to current repository
Svn::Errors Svn::Add(String const &FileName)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// checks whether given file/directory exists
if(!Upp::FileExists(FileName) && !Upp::DirectoryExists(FileName))
return SetError(Svn::PathNotFound);
// checks whether given path is inside repository
if(!IsLocalPath(FileName))
return SetError(Svn::PathNotInRepo);
// now we can add file to repository
result = ExecSvn("add -q " + FileName, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
return SetError(Svn::Ok);
} // END Svn::Add()
Svn::Errors Svn::Add(Array<String> const &FileNames)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// checks if all furnished files/directories exists and are in local repository
for(int i = 0; i < FileNames.GetCount(); i++)
{
if(!Upp::FileExists(FileNames[i]) && !Upp::DirectoryExists(FileNames[i]))
return SetError(Svn::PathNotFound);
if(!IsLocalPath(FileNames[i]))
return SetError(Svn::PathNotInRepo);
}
// now we can add files to repository
for(int i = 0; i < FileNames.GetCount(); i++)
{
result = ExecSvn("add -q " + FileNames[i], Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
}
return SetError(Svn::Ok);
} // END Svn::Add()
///////////////////////////////////////////////////////////////////////////////////////////////
// removes file(s) from current repository
Svn::Errors Svn::Delete(String const &FileName)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// must NOT check if files exists, they can be deleted
// from folder but be listed in repo
// checks whether given path is inside repository
if(!IsLocalPath(FileName))
return SetError(Svn::PathNotInRepo);
// now we can remove file to repository
result = ExecSvn("delete -q " + FileName, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
return SetError(Svn::Ok);
} // END Svn::Delete()
Svn::Errors Svn::Delete(Array<String> const &FileNames)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// must NOT check if files exists, they can be deleted
// from folder but be listed in repo
// checks if all furnished files/directories are in local repository
for(int i = 0; i < FileNames.GetCount(); i++)
{
if(!IsLocalPath(FileNames[i]))
return SetError(Svn::PathNotInRepo);
}
// now we can add files to repository
for(int i = 0; i < FileNames.GetCount(); i++)
{
result = ExecSvn("delete -q " + FileNames[i], Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
}
return SetError(Svn::Ok);
} // END Svn::Delete()
///////////////////////////////////////////////////////////////////////////////////////////////
// copy a file inside current repository
Svn::Errors Svn::Copy(String const &Src, String const &Dest)
{
String Out, Err;
Svn::Errors result;
String AuthStr;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// first, checks that given paths are inside current repository
if(!IsRepositoryPath(Src) || !IsRepositoryPath(Dest))
return SetError(Svn::PathNotInRepo);
// if source path is local, checks if exists
if(!IsUrl(Src) && !Upp::FileExists(Src) && !Upp::DirectoryExists(Src))
return SetError(Svn::PathNotFound);
// dest path is local, it should not exist
if(!IsUrl(Dest) && (Upp::FileExists(Dest) || Upp::DirectoryExists(Dest)))
return SetError(Svn::PathExists);
// if source or dest are remote, may require authentication
if(IsUrl(Src) || IsUrl(Dest))
AuthStr = BuildUserPassword();
else
AuthStr = " ";
// now we can do the copy
result = ExecSvn("copy -q " + AuthStr + Src + " " + Dest, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
return SetError(Svn::Ok);
} // END Svn::Copy()
///////////////////////////////////////////////////////////////////////////////////////////////
// moves a file inside current repository
Svn::Errors Svn::Move(String const &Src, String const &Dest)
{
String Out, Err;
Svn::Errors result;
String AuthStr;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// first, checks that given paths are inside current repository
if(!IsRepositoryPath(Src) || !IsRepositoryPath(Dest))
return SetError(Svn::PathNotInRepo);
// if source path is local, checks if exists
if(!IsUrl(Src) && !Upp::FileExists(Src) && !Upp::DirectoryExists(Src))
return SetError(Svn::PathNotFound);
// dest path is local, it should not exist
if(!IsUrl(Dest) && (Upp::FileExists(Dest) || Upp::DirectoryExists(Dest)))
return SetError(Svn::PathExists);
// if source or dest are remote, may require authentication
if(IsUrl(Src) || IsUrl(Dest))
AuthStr = BuildUserPassword();
else
AuthStr = " ";
// now we can do the move
result = ExecSvn("move -q " + AuthStr + Src + " " + Dest, Out, Err, false);
if(result != Svn::Ok)
return SetError(result);
return SetError(Svn::Ok);
} // END Svn::Move()
///////////////////////////////////////////////////////////////////////////////////////////////
// gets log from remote
Svn::Errors Svn::GetLog(Array<Svn::LogInfo>&logArray)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// executes 'svn log' command -- gets current checked-in information
result = ExecSvn("log " + BuildUserPassword() + FLocalPath, Out, Err, true);
if(result != Svn::Ok)
return SetError(result);
// parses log into logArray
return SetError(ParseLog(Out, logArray));
} // END Svn::GetLog()
///////////////////////////////////////////////////////////////////////////////////////////////
// gets local repository status
Svn::Errors Svn::GetStatus(Array<Svn::StatusInfo>&statusArray)
{
String Out, Err;
Svn::Errors result;
// if not connected, does nothing
if(!FConnected)
return SetError(Svn::NotConnected);
// executes 'svn status' command -- gets current checked-in information
result = ExecSvn("status " + FLocalPath, Out, Err, true);
if(result != Svn::Ok)
return SetError(result);
// parses log into logArray
return SetError(ParseStatus(Out, statusArray));
} // END Svn::GetStatus()
///////////////////////////////////////////////////////////////////////////////////////////////
// updates info from current repository
Svn::Errors Svn::UpdateInfo(void)
{
} // END Svn::UpdateInfo()
END_UPP_NAMESPACE

277
bazaar/SvnLib/SvnLib.h Normal file
View file

@ -0,0 +1,277 @@
#ifndef _SvnLib_h_
#define _SvnLib_h_
#include <Core/Core.h>
NAMESPACE_UPP
//////////////////////////////////////////////////////////////////////////////////////////////
// svn interface class
// encapsulates svn operation using command line tool 'svn'
class Svn
{
public:
// types of svn nodes
enum NodeTypes
{
SvnUnknown,
SvnDir
};
// errors returned by various svn functions
enum Errors
{
Ok = 0,
UnknownError,
NoSvnApp,
NotARepo,
NotConnected,
FileNotFound,
PathNotFound,
FileExists,
DirectoryExists,
PathExists,
DirectoryNotEmpty,
NotADirectory,
CantRemoveDirectory,
CantWriteDirectory,
PathNotInRepo,
NotAnURL,
BadXml,
BadLogin
};
// Human-readable error messages
static const char *ErrorMessages[];
// Item status
enum ItemStatus
{
unknown,
added,
missing,
incomplete,
replaced,
modified,
merged,
conflicted,
obstructed,
ignored,
external,
unversioned,
}; // END enum ItemStatus
// Human-readable status strings
static const char *StatusStrings[];
static const int NumStatus;
// from status string, gets status
static ItemStatus String2Status(String const &s);
// from status, gets human-readable string
static String Status2String(ItemStatus s);
// svn entry info
struct EntryInfo
{
String kind;
String path;
long revision;
String url;
String repositoryRoot;
String repositoryUUID;
long commitRevision;
String commitAuthor;
String commitDate;
}; // END struct EntryInfo;
// svn log info
struct LogInfo
{
long revision;
String author;
String message;
String date;
}; // END struct LogInfo
// svn status info
struct StatusInfo
{
String path;
String props;
ItemStatus status;
long revision;
long commitRevision;
String commitAuthor;
String commitDate;
}; // END struct StatusInfo
private:
// log-in stuffs
String FUserName;
String FPassword;
bool FAnonymous;
// svn repository info stuffs
String FLocalPath;
String FRepositoryRoot;
String FRepositoryUUID;
String FCheckedRevisionAuthor;
ulong FCheckedRevision;
String FCheckedRevisionDate;
String FHeadRevisionAuthor;
ulong FHeadRevision;
String FHeadRevisionDate;
bool FConnected;
Errors FLastError;
String FLastCommandOutput;
String FLastCommandError;
// executes svn, passing cmdline as argument, gathering output in Output
// in xml format (where available) if xml is true
Svn::Errors ExecSvn(String const &CmdLine, String &OutStr, String &ErrStr, bool xml = false);
// parses output of 'svn info' command and gathers its output
Errors ParseInfo(String const &str, EntryInfo &info);
// parses xml output of 'svn log' command
Errors ParseLog(String const &str, Array<LogInfo> &info);
// parses xml output of 'svn status' command
Errors ParseStatus(String const &str, Array<StatusInfo> &info);
// builds username/password string
String BuildUserPassword(void);
// builds revision string
String BuildRevision(String const &Revision);
// checks whether a string is a remote URL or a local path
bool IsUrl(String const &path);
// checks whether a path is inside local repository
bool IsLocalPath(String const &path);
// checks whether a path is inside remote repository
bool IsRemotePath(String const &path);
// checks whether a path is inside repository, remote or local side
bool IsRepositoryPath(String const &path);
// initializer... used by constructor and on disconnection
void Init(void);
public:
// constructor
Svn();
// destructor
~Svn();
// sets error condition
Errors SetError(Errors e) { FLastError = e; return e; }
// checks wether a path is a svn
bool IsSvn(String const &LocalPath);
// checks wether the repository is connected
bool IsConnected(void) { return FConnected; }
// gets local path of repository
String GetLocalPath() { return FLocalPath; }
String GetRepositoryRoot() { return FRepositoryRoot; }
String GetRepositoryUUID() { return FRepositoryUUID; }
String GetCheckedRevisionAuthor() { return FCheckedRevisionAuthor; }
ulong GetCheckedRevision() { return FCheckedRevision; }
String GetCkeckedRevisionDate() { return FCheckedRevisionDate; }
String GetHeadRevisionAuthor() { return FHeadRevisionAuthor; }
ulong GetHeadRevision() { return FHeadRevision; }
String GetHeadRevisionDate() { return FHeadRevisionDate; }
Errors GetLastError() { return FLastError; }
String GetErrorMessage(Svn::Errors err) { return ErrorMessages[err]; }
String GetErrorMessage(void) { return ErrorMessages[FLastError]; }
String GetLastCommandOutput() { return FLastCommandOutput; }
String GetLastCommandError() { return FLastCommandError; }
// sets user authentication
void SetUser(String const &UserName, String const &Password);
void SetUser(void);
// connects to the local copy
Errors Connect(String const &LocalPath);
// disconnects from local copy
Errors Disconnect(void);
// checks out from server
Errors Checkout(String const &Url, String const &LocalPath, String const &Revision = "");
// updates current copy from server
Errors Update(String const &Revision = "");
// commits to server
Errors Commit(String const &Message);
// adds file(s) to current repository
Errors Add(String const &FileName);
Errors Add(Array<String> const &FileNames);
// removes file(s) from current repository
Errors Delete(String const &FileName);
Errors Delete(Array<String> const &FileNames);
// copy a file inside current repository
Errors Copy(String const &Src, String const &Dest);
// moves a file inside current repository
Errors Move(String const &Src, String const &Dest);
// gets log from remote
Errors GetLog(Array<Svn::LogInfo>&logArray);
// gets local repository status
Errors GetStatus(Array<Svn::StatusInfo>&statusArray);
// updates info from current repository
Errors UpdateInfo(void);
}; // END Class Svn
/*
blame
cat
cleanup
diff
export
import
list
lock
log
merge
mkdir
propdel (pdel, pd)
propedit (pedit, pe)
propget (pget, pg)
proplist (plist, pl)
propset (pset, ps)
resolved
revert
status (stat, st)
switch (sw)
unlock
*/
END_UPP_NAMESPACE
#endif

8
bazaar/SvnLib/SvnLib.upp Normal file
View file

@ -0,0 +1,8 @@
uses
CtrlLib,
SysExec;
file
SvnLib.h,
SvnLib.cpp;

44
bazaar/SvnTest/SvnTest.h Normal file
View file

@ -0,0 +1,44 @@
#ifndef _SvnTest_SvnTest_h
#define _SvnTest_SvnTest_h
#include <CtrlLib/CtrlLib.h>
#include <SvnLib/SvnLib.h>
using namespace Upp;
#define LAYOUTFILE <SvnTest/SvnTest.lay>
#include <CtrlCore/lay.h>
class SvnTest : public WithSvnTestLayout<TopWindow>
{
// test repository path and local path
String DestPath;
String RepositoryUrl;
// svn object
Svn svn;
// button handlers
void Checkout(void);
void Connect(void);
void Disconnect(void);
void Modify(void);
void GetLog(void);
void GetStatus(void);
void ListFiles(void);
void WipeFolder(void);
void Quit(void);
// error messagebox
void ShowError(void);
void ShowError(String const &msg);
public:
typedef SvnTest CLASSNAME;
SvnTest();
};
#endif

View file

@ -0,0 +1,13 @@
LAYOUT(SvnTestLayout, 672, 396)
ITEM(DocEdit, TextArea, SetFont(CourierZ(10)).WantFocus(false).SetEditable(false).HSizePosZ(4, 4).VSizePosZ(4, 32))
ITEM(Button, CheckoutButton, SetLabel(t_("Checkout")).LeftPosZ(16, 56).BottomPosZ(9, 15))
ITEM(Button, QuitButton, SetLabel(t_("Quit")).LeftPosZ(604, 56).BottomPosZ(9, 15))
ITEM(Button, ModifyButton, SetLabel(t_("Modify files")).LeftPosZ(216, 64).TopPosZ(372, 15))
ITEM(Button, DisconnectButton, SetLabel(t_("Disconnect")).HSizePosZ(144, 464).TopPosZ(372, 15))
ITEM(Button, StatusButton, SetLabel(t_("Get status")).HSizePosZ(352, 264).TopPosZ(372, 15))
ITEM(Button, ListButton, SetLabel(t_("List files")).LeftPosZ(416, 56).TopPosZ(372, 15))
ITEM(Button, WipeButton, SetLabel(t_("Wipe test folder")).LeftPosZ(508, 84).TopPosZ(372, 15))
ITEM(Button, LogButton, SetLabel(t_("Get log")).LeftPosZ(288, 56).TopPosZ(372, 15))
ITEM(Button, ConnectButton, SetLabel(t_("Connect")).HSizePosZ(80, 536).TopPosZ(372, 15))
END_LAYOUT

View file

@ -0,0 +1,12 @@
uses
CtrlLib,
SvnLib;
file
SvnTest.h,
main.cpp,
SvnTest.lay;
mainconfig
"" = "GUI";

269
bazaar/SvnTest/main.cpp Normal file
View file

@ -0,0 +1,269 @@
#include "SvnTest.h"
///////////////////////////////////////////////////////////////////////////////////////////////
// constructor
SvnTest::SvnTest()
{
// initializes repository and local copy locations
DestPath = "/tmp/svn-test";
RepositoryUrl = "https://upp.svn.sourceforge.net/svnroot/upp/trunk/linux_scripts";
// connects the layout
CtrlLayout(*this, "Window title");
// sets up handlers
CheckoutButton <<= THISBACK(Checkout);
ConnectButton <<= THISBACK(Connect);
DisconnectButton <<= THISBACK(Disconnect);
ModifyButton <<= THISBACK(Modify);
LogButton <<= THISBACK(GetLog);
StatusButton <<= THISBACK(GetStatus);
ListButton <<= THISBACK(ListFiles);
WipeButton <<= THISBACK(WipeFolder);
QuitButton <<= THISBACK(Quit);
}
///////////////////////////////////////////////////////////////////////////////////////////////
// handlers
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::Checkout(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Checking out '" + RepositoryUrl + "' repository... please wait\n\n");
TextArea.Sync();
// error if already connected
if(svn.IsConnected())
{
ShowError("Already connected!!!");
return;
}
// connects to repository
if(svn.Checkout(RepositoryUrl, DestPath) == Svn::Ok)
{
StringStream s;
s << "Local path : " << svn.GetLocalPath() << "\n";
s << "Repository root : " << svn.GetRepositoryRoot() << "\n";
s << "Checked in revision : " << AsString(svn.GetCheckedRevision()) << "\n";
s << "Author : " << svn.GetCheckedRevisionAuthor() << "\n";
s << "Head revision : " << svn.GetHeadRevision() << "\n";
s << "Revision Author : " << svn.GetHeadRevisionAuthor() << "\n";
TextArea.Set(TextArea.Get() + s);
}
else
ShowError();
} // END SvnTest::Checkout()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::Connect(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Connecting to local repository '" + DestPath + "' ... please wait\n\n");
TextArea.Sync();
// error if already connected
if(svn.IsConnected())
{
ShowError("Already connected!!!");
return;
}
// connects to repository
if(svn.Connect(DestPath) == Svn::Ok)
{
StringStream s;
s << "Local path : " << svn.GetLocalPath() << "\n";
s << "Repository root : " << svn.GetRepositoryRoot() << "\n";
s << "Checked in revision : " << AsString(svn.GetCheckedRevision()) << "\n";
s << "Author : " << svn.GetCheckedRevisionAuthor() << "\n";
s << "Head revision : " << svn.GetHeadRevision() << "\n";
s << "Revision Author : " << svn.GetHeadRevisionAuthor() << "\n";
TextArea.Set(TextArea.Get() + s);
}
else
ShowError();
} // END SvnTest::Connect()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::Disconnect(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Disconnecting from local repository '" + DestPath + "' ... please wait\n\n");
TextArea.Sync();
// disconnects from repository
if(svn.Disconnect() == Svn::Ok)
TextArea.Set(TextArea.Get() + "Success.\n");
else
ShowError();
} // END SvnTest::Disconnect()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::Modify(void)
{
// changes readme file
String s = LoadFile(DestPath + "/README") + "\nA dummy change";
SaveFile(DestPath + "/README", s);
// removes dobeta file
FileDelete(DestPath + "/dobeta");
// adds an unversioned file
SaveFile(DestPath + "/UnversionedFile", s);
// adds a versioned file
SaveFile(DestPath + "/VersionedFile", s);
svn.Add(DestPath + "/VersionedFile");
} // END SvnTest::Modify()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::GetLog(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Getting log from remote repository '" + RepositoryUrl + "' repository... please wait\n\n");
TextArea.Sync();
// gets log from svn repository
TextArea.Set(TextArea.Get() + " ----- LOG OUTPUT -----\n");
Array<Svn::LogInfo>logArray;
if(svn.GetLog(logArray) == Svn::Ok)
{
StringStream s;
for(int i = 0; i< logArray.GetCount(); i++)
{
s << "Revision : " << logArray[i].revision << "\n";
s << "Author : " << logArray[i].author << "\n";
s << "Message : " << logArray[i].message << "\n";
s << "Date : " << logArray[i].date << "\n";
s << "-------------------------------------------------------------\n";
}
TextArea.Set(TextArea.Get() + s);
}
else
ShowError();
} // END SvnTest::GetLog()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::GetStatus(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Getting status from local repository '" + DestPath + "' ... please wait\n\n");
TextArea.Sync();
// gets repository status
TextArea.Set(TextArea.Get() + " ----- STATUS OUTPUT -----\n");
Array<Svn::StatusInfo>statusArray;
if(svn.GetStatus(statusArray) == Svn::Ok)
{
StringStream s;
for(int i = 0; i< statusArray.GetCount(); i++)
{
s << "Path : " << statusArray[i].path << "\n";
s << "Properties : " << statusArray[i].props << "\n";
s << "Status : " << svn.Status2String(statusArray[i].status) << "\n";
s << "Revision : " << statusArray[i].revision << "\n";
s << "Commit Revision : " << statusArray[i].commitRevision << "\n";
s << "Commit Author : " << statusArray[i].commitAuthor << "\n";
s << "Commit Date : " << statusArray[i].commitDate << "\n";
s << "-------------------------------------------------------------\n";
}
TextArea.Set(TextArea.Get() + s);
}
else
ShowError();
} // END SvnTest::GetStatus()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::ListFiles(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "List of files in test folder:\n\n");
TextArea.Sync();
StringStream s;
Array<FileSystemInfo::FileInfo> files = StdFileSystemInfo().Find(DestPath + "/*");
for(int i = 0; i < files.GetCount(); i++)
s << files[i].filename << "\n";
TextArea.Set(TextArea.Get() + s);
} // END SvnTest::ListFiles()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::WipeFolder(void)
{
// clears text area
TextArea.Clear();
// sets up a welcome message
TextArea.Set(TextArea.Get() + "Wiping test folder '" + DestPath + "'.....\n\n");
TextArea.Sync();
DeleteFolderDeep(DestPath);
TextArea.Set(TextArea.Get() + "Success.\n");
} // END SvnTest::WipeFolder()
///////////////////////////////////////////////////////////////////////////////////////////////
void SvnTest::Quit(void)
{
Break();
} // END SvnTest::Quit()
///////////////////////////////////////////////////////////////////////////////////////////////
// error messagebox
void SvnTest::ShowError(void)
{
StringStream s;
s << "ERROR : " << svn.GetErrorMessage();
TextArea.Set(TextArea.Get() + s + "\n");
TextArea.Sync();
PromptOK(String(s));
} // END SvnTest::ShowError()
void SvnTest::ShowError(String const &str)
{
StringStream s;
s << "ERROR : " << str;
TextArea.Set(TextArea.Get() + s + "\n");
TextArea.Sync();
PromptOK(String(s));
} // END SvnTest::ShowError()
GUI_APP_MAIN
{
SvnTest().Run();
}