#include "svn.h" #include #include /// /// \brief SVN_CMD_COMMIT Command line template to make svn commit /// /// % fields should be replaced by command parameters as following using arg method.: /// %1: Commit message const QString SVN_CMD_COMMIT = QString("commit -m \"%1\""); /// %1: Path of file(s) to lock const QString SVN_CMD_LOCK = QString("lock"); const QString SVN_CMD_UNLOCK = QString("unlock "); /// /// \brief SVN_CMD_CHECKOUT Extract working copy from svn repository. /// /// % fields should be replaced by command parameters as following using arg method.: /// %1: Svn repository(ies) to be checkout. /// Examples: /// \verbatim /// Extract two repositories into the same folder: /// %1 = "svn://10.10.1.2/project/svn/doc" "svn://10.10.1.3/project2/svn/src" /// /// Extract one repository: /// %1 = "svn://10.10.1.2/project/svn/doc" /// /// Extract one repository from file system : /// %1 = "file:///D:/svn_repos/path_of_repository" /// \endverbatim const QString SVN_CMD_CHECKOUT = QString("checkout \"%1\" "); const QString SVN_CMD_ADD = QString("add"); const QString SVN_CMD_REVERT = QString("revert"); const QString SVN_CMD_UPDATE = QString("update"); const QString SVN_CMD_CLEAN_UP = QString("cleanup"); const QString SVN_CMD_LOG = QString("log"); const QString SVN_CMD_INFO = QString("info"); const QStringList VERBOSE = QStringList(); Svn::Svn(QObject *parent) : QObject(parent), m_svnPath(QFileInfo("C:/Program Files/SlikSvn/bin/svn.exe")), m_useDefaultCredential(true) { init(); } Svn::Svn(const QString &svnPath, QObject *parent) : QObject(parent), m_svnPath(svnPath), m_useDefaultCredential(true) { init(); } Svn::Svn(const QString &svnPath, const QString &userName, const QString &password, QObject *parent) : QObject(parent), m_password(password), m_userName(userName), m_svnPath(QFileInfo(svnPath)), m_useDefaultCredential(true) { init(); } Svn::Svn(const QString &userName, const QString &password, QObject *parent) : QObject(parent), m_password(password), m_userName(userName), m_svnPath(QFileInfo("X:/SVN/BU_SC/trunk/Applications/Desktop/QCreateSetup/Components/08_Svn/bin/svn.exe")), m_useDefaultCredential(true) { init(); } void Svn::init(){ if(!m_svnPath.exists()){ qDebug() << "Cannot find svn.exe in \"" << m_svnPath.absoluteFilePath() << "\" !"; } QProcessEnvironment env = svnProcess.processEnvironment(); // Add an environment variable to force english language // this helps to parse result of commands env.insert("LC_MESSAGES", "C"); svnProcess.setProcessEnvironment(env); m_svnProcessState = QProcess::NotRunning; connect(&svnProcess, SIGNAL(error(QProcess::ProcessError)), this, SLOT(processError(QProcess::ProcessError))); connect(&svnProcess, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(finished(int, QProcess::ExitStatus))); connect(&svnProcess, SIGNAL(stateChanged(QProcess::ProcessState)), this, SLOT(processStateChanged(QProcess::ProcessState))); connect(&svnProcess, SIGNAL(readyReadStandardError()), this, SLOT(processReadyReadStandardError())); connect(&svnProcess, SIGNAL(readyReadStandardOutput()), this, SLOT(processReadyReadStandardOutput())); } bool Svn::process(const QString &cmd, const QStringList &paths) { //Check if alreay running if(!checkIsRunning()) return false; //Check path of SVN executable if(!checkSvnPath()) return false; m_errorsDuringProcess.clear(); m_outputDuringProcess.clear(); QDateTime start = QDateTime::currentDateTime(); QString finalCmd(m_svnPath.absoluteFilePath()); finalCmd.append(" "); finalCmd.append(cmd); //Avoid user ineraction dialogs if(!finalCmd.contains("--non-interactive")) finalCmd.append(" --non-interactive"); //Enable verbose mode //command doesn't support the versbose option if(VERBOSE.contains(cmd) && verbose() && !finalCmd.contains("-v") && !finalCmd.contains("--verbose")){ finalCmd.append(" -v"); } //Append path at the end of the command finalCmd.append(QString(" ") + getPath(paths)); qDebug() << "Send command: " << finalCmd; emit message(QString("Send command: ") + finalCmd); svnProcess.start(finalCmd); svnProcess.waitForFinished(-1); return true; } QString Svn::getPath(const QStringList &paths) { QString pathsString; QStringList temp = paths.isEmpty() ? m_currentPaths : paths; foreach(const QString &path, temp){ //if(path.contains("*") || path.contains("svn://") || path.contains("http://") || path.con) QFileInfo pathInfo = QFileInfo(path); if(!pathInfo.exists()){ pathsString.append(path); } else { pathsString.append(QString(" \"") + (pathInfo.isFile() ? pathInfo.absoluteFilePath() : pathInfo.absolutePath()) + QString("\"")); } } return pathsString; } Svn::~Svn() { if(svnProcess.state() != QProcess::NotRunning){ //Let 1 sec to the process to finish before to kill it svnProcess.waitForFinished(1000); } } QString Svn::password() const { return m_password; } QString Svn::userName() const { return m_userName; } bool Svn::useDefaultCredential() const { return m_useDefaultCredential; } QString Svn::svnPath() const { return m_svnPath.absoluteFilePath(); } QStringList Svn::currentPaths() const { return m_currentPaths; // if(m_currentPaths.isFile()) // return m_currentPaths.canonicalFilePath(); // else // return m_currentPaths.canonicalPath(); } bool Svn::isRunning() const { if(svnProcess.state() == QProcess::NotRunning) return false; else return true; } bool Svn::verbose() const { return m_verbose; } bool Svn::checkIsRunning() { if(isRunning()){ return throwError(tr("SVN process is already running")); } return true; } bool Svn::checkSvnPath() { if(!QFileInfo(m_svnPath).exists()) return throwError(tr("Cannot find svn executable at this path: %1").arg(m_svnPath.absoluteFilePath())); return true; } void Svn::setPassword(const QString &password) { if (m_password == password) return; m_password = password; emit passwordChanged(password); } void Svn::setUserName(const QString &userName) { if (m_userName == userName) return; m_userName = userName; emit userNameChanged(userName); } void Svn::setUseDefaultCredential(bool enable) { if (m_useDefaultCredential == enable) return; m_useDefaultCredential = enable; emit useDefaultCredentialChanged(enable); } void Svn::setSvnPath(const QString &path) { QFileInfo newPath(path); if (m_svnPath == newPath) return; m_svnPath = newPath; emit svnPathChanged(path); } bool Svn::lock(const QStringList &paths, const QString &logMessage, bool force) { QString options; if(!logMessage.isEmpty()) options.append(QString(" -m \"%1\"").arg(logMessage)); if(force) options.append(" --force"); return process(SVN_CMD_LOCK + options, paths); } bool Svn::unlock(const QStringList &paths) { return process(SVN_CMD_UNLOCK, paths); } bool Svn::commit(const QString &logMessage, const QStringList &paths) { // see http://svnbook.red-bean.com/nightly/fr/svn.ref.svn.c.commit.html for more details //commit command //Syntax: // svn commit [PATH...] -m "message" return process(SVN_CMD_COMMIT.arg(logMessage), paths); } bool Svn::checkout(const QStringList &svnUrls, const QStringList &paths) { return process(SVN_CMD_CHECKOUT.arg(svnUrls.join(" ")), paths); } bool Svn::add(const QStringList &paths, bool recursive, bool emptyFolder) { QString options; if(recursive) options.append(" --force"); else if(emptyFolder) options.append(" --depth empty"); return process(SVN_CMD_ADD + options, paths); } bool Svn::info(const QStringList &paths) { return process(SVN_CMD_INFO, paths); } bool Svn::revert(const QStringList &paths) { return process(SVN_CMD_REVERT, paths); } bool Svn::update(const QStringList &paths, int revision) { QString options; if(revision > 0) options.append(QString(" -r%1").arg(revision)); return process(SVN_CMD_UPDATE + options, paths); } bool Svn::cleanUp(const QStringList &paths) { return process(SVN_CMD_CLEAN_UP, paths); } bool Svn::log(const QStringList &paths) { return process(SVN_CMD_LOG, paths); } void Svn::setCurrentPaths(const QStringList ¤tPaths) { if (m_currentPaths == currentPaths) return; m_currentPaths = currentPaths; emit currentPathChanged(currentPaths); } void Svn::setVerbose(bool verbose) { if (m_verbose == verbose) return; m_verbose = verbose; emit verboseChanged(verbose); } void Svn::processError(QProcess::ProcessError e) { emit error(tr("QProcess error append. Code %1").arg(e)); } void Svn::finished(int exitCode, QProcess::ExitStatus exitStatus) { if(exitStatus == QProcess::NormalExit && exitCode == 0) emit finished(true); else { emit finished(false); emit error(tr("The process %1 with error code %2") .arg(exitStatus == QProcess::NormalExit ? tr("finished normaly") : tr("crashed")) .arg(exitCode)); } emit isRunningChanged(false); } void Svn::processStateChanged(QProcess::ProcessState state) { //TODO: emit debug messages instead of normal messages // switch(state){ // case QProcess::NotRunning: emit message(tr("The process is not running.")); // break; // case QProcess::Starting: emit message(tr("The process is starting, but the program has not yet been invoked.")); // break; // case QProcess::Running: emit message(tr("The process is running and is ready for reading and writing.")); // break; // } if(m_svnProcessState != QProcess::NotRunning && state == QProcess::NotRunning) emit isRunningChanged(false); else if(m_svnProcessState == QProcess::NotRunning && state != QProcess::NotRunning) emit isRunningChanged(true); } void Svn::processReadyReadStandardError() { QString error = QString::fromStdString(svnProcess.readAllStandardError().toStdString()); //TODO: Do not emit an error message but a debug message instead throwError(error); m_errorsDuringProcess << error; } void Svn::processReadyReadStandardOutput() { QString output = QString::fromStdString(svnProcess.readAllStandardOutput().toStdString()); //TODO: do not emit raw message but parse message to provide message only if necessary and eventually debug messages emit message(output); m_outputDuringProcess << output; } bool Svn::throwError(const QString &e) { emit error(e); m_lastError = e; return false; } //int Svn::timeout() const //{ // return m_timeout; //} //void Svn::setTimeout(int timeout) //{ // m_timeout = timeout; //}