# # # patch "src/monotone/WorkspaceCommitter.cpp" # from [2c27ad30e62fd2782321ae897406b9f095ec5c53] # to [fec464ea30accdfdf993c11c1479f4b5303288a6] # # patch "src/monotone/WorkspaceCommitter.h" # from [360938d22820d95f116691a962b6cb92ce9bc771] # to [79db6a761846ccce23232d750014ec74b9134746] # ============================================================ --- src/monotone/WorkspaceCommitter.cpp 2c27ad30e62fd2782321ae897406b9f095ec5c53 +++ src/monotone/WorkspaceCommitter.cpp fec464ea30accdfdf993c11c1479f4b5303288a6 @@ -17,201 +17,194 @@ * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ - + #include "WorkspaceCommitter.h" -#include "MonotoneDelegate.h" -#include "Guitone.h" +#include "MonotoneUtil.h" #include "BasicIOParser.h" #include "BasicIOWriter.h" -#include #include -WorkspaceCommitter::WorkspaceCommitter(QObject * parent, QString k, QString b, QString a, QString c) - : QObject(parent), key(k) +WorkspaceCommitter::WorkspaceCommitter(const WorkspacePath & workspace) + : QObject(0), workspacePath(workspace) {} + +WorkspaceCommitter::~WorkspaceCommitter() {} + +bool WorkspaceCommitter::run(const QString & key, const QString & branch, + const QString & author, const QString & changelog) { + // + // Prepare the data + // + + QMap certs; + // use the signing key as default author cert value - if (a.isEmpty()) a = k; - - certs.insert("branch", b); - certs.insert("author", a); - certs.insert("changelog", c); - certs.insert("date", + if (author.isEmpty()) + certs.insert("author", key); + else + certs.insert("author", author); + + certs.insert("branch", branch); + certs.insert("changelog", changelog); + certs.insert("date", QDateTime::currentDateTime().toUTC().toString(Qt::ISODate)); - - workspaceDir = QDir(MTN(this)->getNormalizedWorkspacePath()); + + QDir workspaceDir(workspacePath); I(workspaceDir.exists()); -} -WorkspaceCommitter::~WorkspaceCommitter() -{ -} + // + // Check the current revision text for file content changes + // -bool WorkspaceCommitter::run() -{ - // at first get the current revision text // FIXME: we don't yet check here if this has change since we openend the // commit dialog! - Monotone * mtn = MTN(this); - - int cmdNum; - mtn->executeCommand(QStringList() << "get_revision", cmdNum); - - QString data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) + MonotoneTask out = MonotoneUtil::runSynchronousWorkspaceTask( + workspacePath, + MonotoneTask(QStringList() << "get_revision") + ); + if (!out.isFinished()) F("task aborted"); + + // FIXME: this will probably go wrong for non-utf8 encoded filenames! + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { C(QString("Couldn't query workspace revision: %1").arg(data)); return false; } - + QString fullRevision = data; - - BasicIOParser revparser(fullRevision); - if (!revparser.parse()) - { - C("Could not parse basic_io."); + QMap > changedFiles; + + if (!getChangedFiles(fullRevision, changedFiles)) return false; - } - - QList > fileChanges; - - StanzaList stanzas = revparser.getStanzas(); - foreach (Stanza st, stanzas) + + // + // put all found files + // + QMapIterator > it(changedFiles); + while (it.hasNext()) { - QPair pair; - bool isPatch = false; - - foreach (StanzaEntry en, st) - { - if (en.sym == "add_file") - { - pair.first = en.vals.at(0); - fileChanges.append(pair); - break; - } - - if (en.sym == "patch") - { - pair.first = en.vals.at(0); - isPatch = true; - continue; - } - - if (isPatch && en.sym == "from") - { - I(!pair.first.isEmpty()); - I(!en.hash.isNull()); - pair.second = en.hash; - fileChanges.append(pair); - break; - } - } - } - - // put files - for (int i=0, j=fileChanges.size(); i < j; i++) - { - QPair pair = fileChanges.at(i); - if (!putFile(pair.first, pair.second)) - { - // FIXME: what to do with already added file deltas? + it.next(); + // FIXME: what to do with already added file deltas? + if (!putFile(it.key(), it.value().first, it.value().second)) return false; - } } - + + // // commit revision - mtn->executeCommand(QStringList() << "put_revision" << fullRevision, cmdNum); - - data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) + // + // at first get the revision id for later sanity checking + out = MonotoneUtil::runSynchronousWorkspaceTask( + workspacePath, + MonotoneTask(QStringList() << "get_current_revision_id") + ); + if (!out.isFinished()) F("task aborted"); + + data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { + C(QString("Couldn't get current revision").arg(data)); + return false; + } + + data.chop(1); + QString expectedRevisionId = data; + + // FIXME: again, this may cause problems with utf-8 encoded filenames + out = MonotoneUtil::runSynchronousWorkspaceTask( + workspacePath, + MonotoneTask(QStringList() << "put_revision" << fullRevision) + ); + if (!out.isFinished()) F("task aborted"); + + data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) + { C(QString("Couldn't commit revision: %1").arg(data)); return false; } - + data.chop(1); revisionId = data; - I(revisionId.length() == 40); - + + // did put_revision bring the expected result? + if (revisionId.compare(expectedRevisionId) != 0) + { + C(QString("Revision mismatch: expected %1, got %2") + .arg(expectedRevisionId).arg(revisionId)); + return false; + } + // attach certificates QMapIterator i(certs); while (i.hasNext()) { i.next(); - - int cmdNum; - mtn->executeCommand( - QStringList() << "cert" << revisionId << i.key() << i.value(), - QStringList() << "key" << key, - cmdNum - ); - - QString data = mtn->getDecodedData(cmdNum); - - if (mtn->getReturnCode(cmdNum) > 0) - { - C(QString("Couldn't attach cert %1: %2").arg(i.key()).arg(data)); + // FIXME: what to do with already added certs? + if (!putCert(revisionId, key, i.key(), i.value())) return false; - } } - - D(QString("Committed revision %1").arg(revisionId)); - + + D(QString("Successfully committed revision %1").arg(revisionId)); + // update _MTN/revision and _MTN/options - I(workspaceDir.cd("_MTN")); - - return writeRevision() && writeOptions(); + return writeRevision(revisionId) && writeOptions(certs); } -bool WorkspaceCommitter::writeRevision() +bool WorkspaceCommitter::writeRevision(const QString & revisionId) { + QDir workspaceDir(workspacePath); + I(workspaceDir.cd("_MTN")); + QFile file(workspaceDir.filePath("revision")); if (!file.open(QIODevice::WriteOnly)) { C("Can't open revision for writing"); return false; } - + StanzaList stanzas; - + Stanza format; format.append(StanzaEntry("format_version", QStringList() << "1")); stanzas.append(format); - + Stanza manifest; manifest.append(StanzaEntry("new_manifest", "0000000000000000000000000000000000000000")); stanzas.append(manifest); - + Stanza oldrev; oldrev.append(StanzaEntry("old_revision", revisionId)); stanzas.append(oldrev); - + BasicIOWriter writer(stanzas); QString rev = writer.write(); I(file.write(rev.toLatin1())); file.close(); - + return true; } -bool WorkspaceCommitter::writeOptions() +bool WorkspaceCommitter::writeOptions(const QMap & certs) { + QDir workspaceDir(workspacePath); + I(workspaceDir.cd("_MTN")); + QFile file(workspaceDir.filePath("options")); if (!file.open(QIODevice::ReadWrite)) { C("Can't open options for reading and writing"); return false; } - + // read and parse the basic_io stanzas and set the correct branch name BasicIOParser parser(QString::fromUtf8(file.readAll())); I(parser.parse()); StanzaList stanzas = parser.getStanzas(); I(stanzas.size() == 1); - + for (int i=0, j=stanzas[0].size(); iexecuteCommand(command, commandNumber) || - mtn->getReturnCode(commandNumber) > 0) + + MonotoneTask in(command); + MonotoneTask out = MonotoneUtil::runSynchronousWorkspaceTask(workspacePath, in); + if (!out.isFinished()) F("task aborted"); + + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) { - C(QString("Cannot execute put_file: %1") - .arg(mtn->getDecodedData(commandNumber))); + C(QString("Cannot execute put_file: %1").arg(data)); return false; } - + + // chop the newline + data.chop(1); + + // sanity check: has the committed file the same fileid as get_revision + // reported? + if (data.compare(fileId) != 0) + { + C(QString("Wrong fileid for %1: expected %2, got %3") + .arg(fileName).arg(fileId).arg(data)); + return false; + } + return true; } +bool WorkspaceCommitter::putCert(const QString & rev, const QString & commitKey, + const QString & key, const QString & value) +{ + MonotoneTask out = MonotoneUtil::runSynchronousWorkspaceTask( + workspacePath, + MonotoneTask( + QStringList() << "cert" << rev << key << value, + QStringList() << "key" << commitKey + ) + ); + if (!out.isFinished()) F("task aborted"); + + QString data = out.getOutputUtf8(); + if (out.getReturnCode() > 0) + { + C(QString("Couldn't attach cert %1: %2").arg(key).arg(data)); + return false; + } + return true; +} + +bool WorkspaceCommitter::getChangedFiles( + const QString & revisionText, + QMap > & changedFiles) +{ + BasicIOParser revparser(revisionText); + if (!revparser.parse()) + { + C("Could not parse basic_io."); + return false; + } + + StanzaList stanzas = revparser.getStanzas(); + foreach (Stanza st, stanzas) + { + QString path; + QPair hashes; + bool isPatch = false; + bool isAdd = false; + + foreach (StanzaEntry en, st) + { + if (en.sym == "add_file") + { + path = en.vals.at(0); + isAdd = true; + continue; + } + + if (isAdd && en.sym == "content") + { + hashes.first = en.hash; + changedFiles.insert(path, hashes); + break; + } + + if (en.sym == "patch") + { + path = en.vals.at(0); + isPatch = true; + continue; + } + + if (isPatch && en.sym == "from") + { + hashes.second = en.hash; + continue; + } + + if (isPatch && en.sym == "to") + { + hashes.first = en.hash; + changedFiles.insert(path, hashes); + break; + } + } + } + return true; +} + ============================================================ --- src/monotone/WorkspaceCommitter.h 360938d22820d95f116691a962b6cb92ce9bc771 +++ src/monotone/WorkspaceCommitter.h 79db6a761846ccce23232d750014ec74b9134746 @@ -26,24 +26,29 @@ #include #include +// +// TODO: we should make this class working asynchronously and work +// nicely together with QProgressDialog +// class WorkspaceCommitter : public QObject { Q_OBJECT public: - WorkspaceCommitter(QObject *, QString, QString, QString, QString); + WorkspaceCommitter(const WorkspacePath &); ~WorkspaceCommitter(); - bool run(); + + bool run(const QString &, const QString &, const QString &, const QString &); inline QString getRevisionId() const { return revisionId; } -protected: - bool putFile(const QString &, const QString & baseId = QString()); - bool writeRevision(); - bool writeOptions(); - +private: + bool getChangedFiles(const QString &, QMap > &); + bool putFile(const QString &, const QString &, const QString &); + bool putCert(const QString &, const QString &, const QString &, const QString &); + bool writeRevision(const QString &); + bool writeOptions(const QMap &); + QString revisionId; - QString key; - QDir workspaceDir; - QMap certs; + WorkspacePath workspacePath; }; #endif