# # # patch "src/model/Inventory.cpp" # from [268c873ff30bfe89ed35f7f830b1c444cc042978] # to [eb43f55fde6a258cdfa78571dfbafd521188c869] # # patch "src/model/Inventory.h" # from [5f721ad6fa46ed5f994688debe1d0708381607b7] # to [4754ad06964c1507ee319626cbc9a2328334ef99] # # patch "src/monotone/FileExporter.cpp" # from [0f009d4134447bb847670ccbd8cab2c12b5e9923] # to [0c4dbb47600b2a6ac434f58c7412ff920e068b36] # # patch "src/monotone/MonotoneDelegate.cpp" # from [320d6bbac9f99524c2a8867c0161d1d448f48ccb] # to [5d504bf771849cd6eb9969db7ccf4df98142144c] # # patch "src/monotone/MonotoneDelegate.h" # from [466b3b5eedc5747f9fca4b02721042b8c2b78175] # to [4a188eda03b4f743f508cdd05a333102dfeb0337] # # patch "src/vocab.h" # from [41e69475de03ca8f2099076d05ddfa8760b7bb60] # to [13dbae968d4cc0151526afc177a32806630c373a] # ============================================================ --- src/model/Inventory.cpp 268c873ff30bfe89ed35f7f830b1c444cc042978 +++ src/model/Inventory.cpp eb43f55fde6a258cdfa78571dfbafd521188c869 @@ -27,10 +27,10 @@ Inventory::Inventory(QObject *parent) : Inventory::Inventory(QObject *parent) : QAbstractItemModel(parent) { - // create a dummy item since the view needs at least one item - // in the model, otherwise the app crashes - rootItem = new InventoryItem(); - regex = new QRegExp("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$"); + // create a dummy item since the view needs at least one item + // in the model, otherwise the app crashes + rootItem = new InventoryItem(); + regex = new QRegExp("^(R|D|[ ])(R|A|[ ])(M|P|U|I|[ ])\\s(\\d+)\\s(\\d+)\\s(.+)$"); regex->setMinimal(true); mtnDelegate = new MonotoneDelegate(this); @@ -38,8 +38,8 @@ Inventory::~Inventory() Inventory::~Inventory() { - delete rootItem; - delete regex; + delete rootItem; + delete regex; delete mtnDelegate; } @@ -48,58 +48,62 @@ bool Inventory::readInventory() QStringList cmd; cmd << "inventory"; - return mtnDelegate->triggerCommand(cmd); + return mtnDelegate->triggerCommand(cmd); } void Inventory::parseOutput() { - QStringList lines = AutomateCommand::data.split("\n", QString::SkipEmptyParts); - QMap renameMap; - QMap::iterator renameIter; + QStringList lines = AutomateCommand::data.split("\n", QString::SkipEmptyParts); + QMap renameMap; + QMap::iterator renameIter; - InventoryItem * item; - QList tempItems; - int status(0); - int from_id(0); - int to_id(0); - QString path(""); - bool isDirectory(false); + QList items; + InventoryItem * item; + + int status(0); + int from_id(0); + int to_id(0); + QString path(""); + bool isDirectory(false); - for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) - { - if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory)) - { - continue; - } - - // this item is given a parent explicitely later on - item = new InventoryItem(isDirectory); + for (QStringList::Iterator it = lines.begin(); it != lines.end(); ++it) + { + if (!parseInventoryLine(*it, status, from_id, to_id, path, isDirectory)) + { + continue; + } + + // this item is given a parent explicitely later on + item = new InventoryItem(isDirectory); item->setPath(path); item->setStatus(status); - if (from_id > 0) - { - renameMap[-from_id] = item; - } - if (to_id > 0) - { - renameMap[to_id] = item; - } + if (from_id > 0) + { + renameMap[-from_id] = item; + } + if (to_id > 0) + { + renameMap[to_id] = item; + } - tempItems.push_back(item); - } + items.push_back(item); + } - int id = 0; + int id = 0; - while (true) - { - renameIter = renameMap.find(++id); - if (renameIter == renameMap.end()) break; + while (true) + { + renameIter = renameMap.find(++id); + if (renameIter == renameMap.end()) break; - renameMap[id]->setRenamedFrom(renameMap[-id]); - renameMap[-id]->setRenamedTo(renameMap[id]); - } + renameMap[id]->setRenamedFrom(renameMap[-id]); + renameMap[-id]->setRenamedTo(renameMap[id]); + } + flatItemList.clear(); + flatItemList = items; + // FIXME: we shouldn't really add a workspace root item here, but // mtn automate inventory currently doesn't print the root workspace dir InventoryItem * branch = new InventoryItem(true, true); @@ -107,132 +111,131 @@ void Inventory::parseOutput() branch->setPath("."); branch->setStatus(0); branch->setLabel(MonotoneDelegate::getBranchName(this)); - branch->setChildren(buildTreeRecursive(tempItems, NULL)); + branch->setChildren(buildTreeRecursive(items, NULL)); // remove any older item rootItem->deleteAllChildren(); rootItem->appendChild(branch); - // reset the model to repaint the view completly - // (all QModelIndexes are discarded through that, e.g. selections!) - reset(); + // reset the model to repaint the view completly + // (all QModelIndexes are discarded through that, e.g. selections!) + reset(); - // restore the normal cursor - qApp->restoreOverrideCursor(); + // restore the normal cursor + qApp->restoreOverrideCursor(); emit modelCreated(); } -QList Inventory::buildTreeRecursive(QList &items, InventoryItem* parentItem) +QList Inventory::buildTreeRecursive(QList & items, InventoryItem * parentItem) { - QList finalItems; + QList finalItems; - QString parentPath = ""; - int parentStatus = 0; - - if (parentItem != NULL) - { - parentPath = parentItem->getPath(); - parentStatus = parentItem->getStatus(); - } + QString parentPath = ""; - // add pseudo item "cd up" for each directory + if (parentItem != NULL) + { + parentPath = parentItem->getPath(); + } + + // add pseudo item "cd up" for each directory level InventoryItem * cdUp = new InventoryItem(true); cdUp->setParent(parentItem); cdUp->setPath(parentPath + QString("/..")); + finalItems.append(cdUp); - items.prepend(cdUp); - InventoryItem * currentItem; - while (items.size() > 0) - { - currentItem = items.front(); + + while (items.size() > 0) + { + currentItem = items.front(); - // - // if the item is not directly inside the current path stop immediately - // - QString tmpPath = parentPath; - tmpPath.append("/"); - tmpPath.append(currentItem->getFilename()); + // + // if the item is not directly inside the current path stop immediately + // + QString tmpPath = parentPath; + tmpPath.append("/"); + tmpPath.append(currentItem->getFilename()); - // path may not be empty for this condition - if (!parentPath.isEmpty() && currentItem->getPath().compare(tmpPath) != 0) - { - break; - } + // path may not be empty for this condition + if (!parentPath.isEmpty() && currentItem->getPath().compare(tmpPath) != 0) + { + break; + } - // remove the item if we can append it now - items.pop_front(); + // remove the item if we can append it now + items.pop_front(); - // - // it seems to be a valid item - // + // + // it seems to be a valid item + // - // if the item is directory a directory, make sure we catch decendant items... recursion! - if (currentItem->isDirectory() && !currentItem->isCdUp()) - { - currentItem->setChildren(buildTreeRecursive(items, currentItem)); + // if the item is directory a directory, catch items inside it + if (currentItem->isDirectory()) + { + currentItem->setChildren(buildTreeRecursive(items, currentItem)); } - // append item to final list - finalItems.push_back(currentItem); + // append item to final list + finalItems.push_back(currentItem); - } - return finalItems; + } + + return finalItems; } QModelIndex Inventory::index(int row, int column, const QModelIndex &parent) const { - InventoryItem * parentItem; + InventoryItem * parentItem; - if (!parent.isValid()) - { - parentItem = rootItem; - } - else - { - parentItem = static_cast(parent.internalPointer()); - } + if (!parent.isValid()) + { + parentItem = rootItem; + } + else + { + parentItem = static_cast(parent.internalPointer()); + } - InventoryItem * childItem = parentItem->child(row); + InventoryItem * childItem = parentItem->child(row); - if (childItem) - { - return createIndex(row, column, childItem); - } + if (childItem) + { + return createIndex(row, column, childItem); + } - return QModelIndex(); + return QModelIndex(); } int Inventory::columnCount(const QModelIndex &parent) const { - if (parent.isValid()) - { - return static_cast(parent.internalPointer())->columnCount(); - } + if (parent.isValid()) + { + return static_cast(parent.internalPointer())->columnCount(); + } - return rootItem->columnCount(); + return rootItem->columnCount(); } QVariant Inventory::data(const QModelIndex &index, int role) const { - if (!index.isValid()) - { - return QVariant(); - } + if (!index.isValid()) + { + return QVariant(); + } - InventoryItem * item = static_cast(index.internalPointer()); + InventoryItem * item = static_cast(index.internalPointer()); - if ((role == Qt::DecorationRole) && (index.column() == 0)) - { + if ((role == Qt::DecorationRole) && (index.column() == 0)) + { IconProvider * provider = IconProvider::singleton(); - return provider->getIcon(item); - } - else - { - return item->data(index.column(), role); - } + return provider->getIcon(item); + } + else + { + return item->data(index.column(), role); + } } bool Inventory::setData(const QModelIndex & idx, const QVariant & value, int role) @@ -247,7 +250,7 @@ Qt::ItemFlags Inventory::flags(const QMo Qt::ItemFlags Inventory::flags(const QModelIndex &index) const { - if (!index.isValid()) return 0; + if (!index.isValid()) return 0; QFlags flags = Qt::ItemIsSelectable | Qt::ItemIsEnabled; @@ -260,10 +263,10 @@ Qt::ItemFlags Inventory::flags(const QMo flags |= Qt::ItemIsEditable | Qt::ItemIsDragEnabled; if (item->isDirectory()) - { - flags |= Qt::ItemIsDropEnabled; + { + flags |= Qt::ItemIsDropEnabled; } - */ + */ return flags; } @@ -271,137 +274,137 @@ QVariant Inventory::headerData(int secti // the root item needs to store the header for each field QVariant Inventory::headerData(int section, Qt::Orientation orientation, int role) const { - if (orientation == Qt::Horizontal && role == Qt::DisplayRole) - { - return rootItem->data(section, role); - } + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + return rootItem->data(section, role); + } - return QVariant(); + return QVariant(); } QModelIndex Inventory::parent(const QModelIndex& index) const { - if (!index.isValid()) - { - return QModelIndex(); - } + if (!index.isValid()) + { + return QModelIndex(); + } - InventoryItem * childItem = static_cast(index.internalPointer()); - InventoryItem * parentItem = childItem->parent(); + InventoryItem * childItem = static_cast(index.internalPointer()); + InventoryItem * parentItem = childItem->parent(); - if (parentItem == rootItem) - { - return QModelIndex(); - } + if (parentItem == rootItem) + { + return QModelIndex(); + } - return createIndex(parentItem->row(), 0, parentItem); + return createIndex(parentItem->row(), 0, parentItem); } int Inventory::rowCount(const QModelIndex& parent) const { - InventoryItem * parentItem = rootItem; + InventoryItem * parentItem = rootItem; - if (parent.isValid()) - { - parentItem = static_cast(parent.internalPointer()); - } + if (parent.isValid()) + { + parentItem = static_cast(parent.internalPointer()); + } - return parentItem->childCount(); + return parentItem->childCount(); } bool Inventory::parseInventoryLine( - const QString &inputString, - int &status, - int &from_id, - int &to_id, - QString &path, - bool &isDirectory) + const QString &inputString, + int &status, + int &from_id, + int &to_id, + QString &path, + bool &isDirectory) { - if (regex->indexIn(inputString) == -1) - { - qWarning("Couldn't parse inventory line %s", qPrintable(inputString)); - return false; - } + if (regex->indexIn(inputString) == -1) + { + qWarning("Couldn't parse inventory line %s", qPrintable(inputString)); + return false; + } - QStringList list = regex->capturedTexts(); - status = 0; + QStringList list = regex->capturedTexts(); + status = 0; - // the first match - if (list[1].compare("R") == 0) - { - status |= InventoryItem::RenamedFrom; - } else - if (list[1].compare("D") == 0) - { - status |= InventoryItem::Dropped; - } - else - if (list[1].compare(" ") != 0) - { - qWarning("Unknown status first tripel %s", qPrintable(list[1])); - } + // the first match + if (list[1].compare("R") == 0) + { + status |= InventoryItem::RenamedFrom; + } else + if (list[1].compare("D") == 0) + { + status |= InventoryItem::Dropped; + } + else + if (list[1].compare(" ") != 0) + { + qWarning("Unknown status first tripel %s", qPrintable(list[1])); + } - // the second match - if (list[2].compare("R") == 0) - { - status |= InventoryItem::RenamedTo; - } else - if (list[2].compare("A") == 0) - { - status |= InventoryItem::Added; - } - else - if (list[2].compare(" ") != 0) - { - qWarning("Unknown status second tripel %s", qPrintable(list[2])); - } + // the second match + if (list[2].compare("R") == 0) + { + status |= InventoryItem::RenamedTo; + } else + if (list[2].compare("A") == 0) + { + status |= InventoryItem::Added; + } + else + if (list[2].compare(" ") != 0) + { + qWarning("Unknown status second tripel %s", qPrintable(list[2])); + } - // the third match - if (list[3].compare("M") == 0) - { - status |= InventoryItem::Missing; - } else - if (list[3].compare("P") == 0) - { - status |= InventoryItem::Patched; - } else - if (list[3].compare("U") == 0) - { - status |= InventoryItem::Unknown; - } else - if (list[3].compare("I") == 0) - { - status |= InventoryItem::Ignored; - } + // the third match + if (list[3].compare("M") == 0) + { + status |= InventoryItem::Missing; + } else + if (list[3].compare("P") == 0) + { + status |= InventoryItem::Patched; + } else + if (list[3].compare("U") == 0) + { + status |= InventoryItem::Unknown; + } else + if (list[3].compare("I") == 0) + { + status |= InventoryItem::Ignored; + } else - if (list[3].compare(" ") == 0) - { - status |= InventoryItem::Unchanged; - } + if (list[3].compare(" ") == 0) + { + status |= InventoryItem::Unchanged; + } else - { + { qWarning("Unknown status third tripel %s", qPrintable(list[3])); - } + } Q_ASSERT(InventoryItem::ValidStates.contains(status)); - // now determine if the file has been renamed - from_id = list[4].toInt(); - to_id = list[5].toInt(); + // now determine if the file has been renamed + from_id = list[4].toInt(); + to_id = list[5].toInt(); - // remove trailing slash - path = list[6].trimmed(); + // remove trailing slash + path = list[6].trimmed(); isDirectory = false; - if (path.endsWith('/')) - { - isDirectory = true; - path = path.left(path.length() - 1); - } - - // parsing was successful - return true; + if (path.endsWith('/')) + { + isDirectory = true; + path = path.left(path.length() - 1); + } + + // parsing was successful + return true; } bool Inventory::handleError(int errCode) @@ -422,3 +425,90 @@ bool Inventory::handleError(int errCode) return true; } +QMap Inventory::findUnaccountedRenames() +{ + QList missingItems; + + foreach (InventoryItem * item, flatItemList) + { + // if an item was just added, but is now missing, we have no fileid + // from the base roster, thus cannot find any matches later on + if (item->hasStatus(InventoryItem::Missing) && + item->hasNotStatus(InventoryItem::Added)) + { + missingItems.append(item); + } + } + + // TODO: progess bar here! + + QString parentRev = MonotoneDelegate::getBaseWorkspaceRevision(this); + FileEntryList parentList = + MonotoneDelegate::getRevisionManifest(this, parentRev); + + QMap unaccountedRenames; + + foreach (InventoryItem * item, missingItems) + { + FileEntryList candidates; + + // at first check if the simple case: the item was renamed + // in the bookkeeping dir, but not physically + if (item->hasStatus(InventoryItem::RenamedTo)) + { + InventoryItem * oldItem = item->getRenamedFrom(); + Q_ASSERT(oldItem); + + // yes, there is an unknown path with the same name! + if (oldItem->hasStatus(InventoryItem::Unknown)) + { + bool found = false; + FileEntry entry; + foreach (entry, parentList) + { + if (entry.path == oldItem->getPath()) + { + found = true; + break; + } + } + Q_ASSERT(found); + candidates.append(entry); + } + else + { + continue; + } + } + else + { + // apparently there are is rename info, lets try to find + // the source item "the hard way" + + // TODO: do a path-based comparison for directory items + // since they have no associated fileid + bool found = false; + FileEntry entry; + foreach (entry, parentList) + { + if (entry.path == item->getPath()) + { + found = true; + break; + } + } + Q_ASSERT(found); + + // TODO: for this to work however, we need some kind of + // mtn automate ident command + W(QString("FIXME: iterate over current workspace and " + "find suitable files with %1 for %2") + .arg(entry.fileid).arg(entry.path)); + } + + unaccountedRenames.insert(item->getPath(), candidates); + } + + return unaccountedRenames; +} + ============================================================ --- src/model/Inventory.h 5f721ad6fa46ed5f994688debe1d0708381607b7 +++ src/model/Inventory.h 4754ad06964c1507ee319626cbc9a2328334ef99 @@ -37,9 +37,7 @@ class Inventory : public QAbstractItemMo Inventory(QObject *parent); ~Inventory(); bool readInventory(); - static QString getOption(QObject *, const QString &); - static QString getBranchName(QObject *); - static QString getBranchNameShort(QObject *); + QMap findUnaccountedRenames(); // needed Qt Model methods QVariant data(const QModelIndex&, int) const; @@ -55,13 +53,14 @@ class Inventory : public QAbstractItemMo private: void parseOutput(); bool handleError(int); - bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &); - QList buildTreeRecursive(QList &, InventoryItem*); - - InventoryItem * rootItem; - QRegExp * regex; - MonotoneDelegate * mtnDelegate; - QString branchName; + bool parseInventoryLine(const QString &, int &, int &, int &, QString &, bool &); + QList buildTreeRecursive(QList &, InventoryItem*); + + InventoryItem * rootItem; + QRegExp * regex; + MonotoneDelegate * mtnDelegate; + QString branchName; + QList flatItemList; signals: void modelCreated(); ============================================================ --- src/monotone/FileExporter.cpp 0f009d4134447bb847670ccbd8cab2c12b5e9923 +++ src/monotone/FileExporter.cpp 0c4dbb47600b2a6ac434f58c7412ff920e068b36 @@ -71,9 +71,9 @@ bool FileExporter::exportFile(const File bool FileExporter::exportFile(const FileEntry & entry) { - if (entry.second) + if (entry.is_dir) { - if (!entry.first.isEmpty() && !rootDir.mkpath(entry.first)) + if (entry.path.isEmpty() || !rootDir.mkpath(entry.path)) { C("Cannot create directory"); return false; @@ -85,7 +85,7 @@ bool FileExporter::exportFile(const File int commandNumber; if (!mtn->executeCommand( - QStringList() << "get_file_of" << entry.first, + QStringList() << "get_file_of" << entry.path, QStringList() << "r" << revision, commandNumber) || mtn->getReturnCode(commandNumber) > 0) { @@ -93,7 +93,7 @@ bool FileExporter::exportFile(const File return false; } - QFile file(rootDir.filePath(entry.first)); + QFile file(rootDir.filePath(entry.path)); if (file.exists()) file.remove(); if (!file.open(QIODevice::WriteOnly)) ============================================================ --- src/monotone/MonotoneDelegate.cpp 320d6bbac9f99524c2a8867c0161d1d448f48ccb +++ src/monotone/MonotoneDelegate.cpp 5d504bf771849cd6eb9969db7ccf4df98142144c @@ -127,6 +127,10 @@ QString MonotoneDelegate::getBaseWorkspa } data.chop(1); + + // we cannot handle workspaces with multiple parents yet + Q_ASSERT(data.indexOf('\n') == -1); + return data; } @@ -267,8 +271,15 @@ FileEntryList MonotoneDelegate::getRevis { Monotone * mtn = MTN(obj); + QStringList cmd; + cmd << "get_manifest_of"; + if (!revision.isEmpty()) + { + cmd << revision; + } + int cmdNum; - mtn->executeCommand(QStringList() << "get_manifest_of" << revision, cmdNum); + mtn->executeCommand(cmd, cmdNum); QString data = mtn->getDecodedData(cmdNum); FileEntryList entries; @@ -290,18 +301,40 @@ FileEntryList MonotoneDelegate::getRevis StanzaList stanzas = parser.getStanzas(); foreach (Stanza st, stanzas) { + FileEntry entry; + bool is_item = false; + foreach (StanzaEntry en, st) { - if (en.sym == "format_version") break; - - FileEntry entry; - entry.first = en.vals.at(0); - Q_ASSERT(en.sym == "dir" || en.sym == "file"); - entry.second = en.sym == "dir"; - - entries.append(entry); - break; + if (en.sym == "format_version") + { + Q_ASSERT(en.vals.size() == 1 && en.vals.at(0) == "1"); + break; + } + else + if (en.sym == "dir" || en.sym == "file") + { + is_item = true; + entry.path = en.vals.at(0); + entry.is_dir = en.sym == "dir"; + } + else + if (en.sym == "content") + { + Q_ASSERT(is_item); + entry.fileid = en.hash; + } + else + if (en.sym == "attr") + { + Q_ASSERT(is_item); + Q_ASSERT(en.vals.size() == 2); + entry.attrs.insert(en.vals.at(0), en.vals.at(1)); + } + else + Q_ASSERT(false); } + if (is_item) entries.append(entry); } return entries; } ============================================================ --- src/monotone/MonotoneDelegate.h 466b3b5eedc5747f9fca4b02721042b8c2b78175 +++ src/monotone/MonotoneDelegate.h 4a188eda03b4f743f508cdd05a333102dfeb0337 @@ -45,7 +45,7 @@ public: static QStringList resolveSelector(QObject *, const QString &); static RevisionCerts getRevisionCerts(QObject *, const QString &); static QString getDatabaseFilePath(QObject *); - static FileEntryList getRevisionManifest(QObject *, const QString &); + static FileEntryList getRevisionManifest(QObject *, const QString & rev = QString()); static QStringList getPrivateKeyList(QObject *); private: ============================================================ --- src/vocab.h 41e69475de03ca8f2099076d05ddfa8760b7bb60 +++ src/vocab.h 13dbae968d4cc0151526afc177a32806630c373a @@ -29,12 +29,22 @@ class Guitone; // type definitions // +#include #include #include #include // used for manifest entries, if the bool var is true, the entry is a directory -typedef QPair FileEntry; +struct FileEntry { + FileEntry() {} + FileEntry(QString p, bool d) : path(p), is_dir(d) {} + FileEntry(QString p, bool d, QString f) : path(p), is_dir(d), fileid(f) {} + QString path; + bool is_dir; + QString fileid; + QMap attrs; +}; + typedef QList FileEntryList; // used for revision certs