#
#
# add_file "res/forms/unaccounted_renames.ui"
# content [4f22b29be16ab911fd67766288f0385ffed6e9f1]
#
# add_file "src/view/dialogs/UnaccountedRenames.cpp"
# content [938d5eac2b9884b18445e4eacde26c5d31dea0e8]
#
# add_file "src/view/dialogs/UnaccountedRenames.h"
# content [0691bf54c8ff2d8606e88eb855613a4977ad8b9d]
#
# patch "guitone.pro"
# from [03f06140d2a1fe2ab82feb90a374d8546ab96ab6]
# to [8a3fa0c8ffda3b50a30fe54d2429a62f130b0f9c]
#
# patch "res/forms/main_window.ui"
# from [9f9f41d6a2d521f10475aabdd7e2901b29243dd1]
# to [600472d44fd5cd51100da93909a592d1815e3505]
#
# patch "src/model/Inventory.cpp"
# from [1e554a233746a80cf81f5bac48efd24eeca7b873]
# to [3cceb0b22340f61c7beba6c6d2e05392d1839cc2]
#
# patch "src/model/Inventory.h"
# from [cb7bd39962c8f983a04c7410318e1643fb048ede]
# to [c9834d77c69b3b9d7c6dc35a5a8bc6118a5b01ec]
#
# patch "src/monotone/Monotone.cpp"
# from [0fe70345373d6aa6338a8a237f7d0ddda2a2d308]
# to [e064e50d6f2e22d1c15cd325346311a4ab93e97c]
#
# patch "src/monotone/MonotoneDelegate.cpp"
# from [6fe7d984293395f5d83ffdcf0c6735d93186a6f7]
# to [0b1b0ebfaa468cbf89d3ef32a54e9791405b86f5]
#
# patch "src/monotone/MonotoneDelegate.h"
# from [4a188eda03b4f743f508cdd05a333102dfeb0337]
# to [6971e5c0d79d50fc766f84fb395cf648da80c8e5]
#
# patch "src/view/MainWindow.cpp"
# from [a12fb6d3645a244dadb84a4e44c95bf2f203dae6]
# to [7064dcc81b11c6991521f64da0762beb9a347350]
#
# patch "src/view/MainWindow.h"
# from [38296ee6b6bbd5e84836949b27167abccb20bb6a]
# to [ed423769deea41872ede542b5920a765f790a38a]
#
# patch "src/view/dialogs/FileHistory.cpp"
# from [fb8b038667a771cbaee11c0f60d1269104e774d3]
# to [47ab8b46f70a4e81ecadd0c686f00dc8874c3fde]
#
# patch "src/vocab.h"
# from [65c921759f5565377c8ffe49fb8abbfbb43fe27c]
# to [ed37b5f6deab84d3025ff1bfb2b0341993391751]
#
============================================================
--- res/forms/unaccounted_renames.ui 4f22b29be16ab911fd67766288f0385ffed6e9f1
+++ res/forms/unaccounted_renames.ui 4f22b29be16ab911fd67766288f0385ffed6e9f1
@@ -0,0 +1,98 @@
+
+ UnaccountedRenamesDialog
+
+
+
+ 0
+ 0
+ 454
+ 316
+
+
+
+ Unaccounted renames
+
+
+ :/icons/guitone.png
+
+
+
+ 9
+
+
+ 6
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ -
+
+
+ 0
+
+
+ 6
+
+
-
+
+
+ Qt::Horizontal
+
+
+
+ 40
+ 20
+
+
+
+
+ -
+
+
+ Perform checked renames
+
+
+
+ -
+
+
+ Close
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ closeButton
+ clicked()
+ UnaccountedRenamesDialog
+ accept()
+
+
+ 399
+ 272
+
+
+ 252
+ 154
+
+
+
+
+
============================================================
--- src/view/dialogs/UnaccountedRenames.cpp 938d5eac2b9884b18445e4eacde26c5d31dea0e8
+++ src/view/dialogs/UnaccountedRenames.cpp 938d5eac2b9884b18445e4eacde26c5d31dea0e8
@@ -0,0 +1,104 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#include "UnaccountedRenames.h"
+#include "IconProvider.h"
+
+#include
+#include
+
+UnaccountedRenames::UnaccountedRenames(QWidget * parent, const QMap & r):
+ Dialog(parent), renames(r)
+{
+ setupUi(this);
+ Dialog::init();
+
+ // OSX sheet-alike dialog
+ setWindowFlags(Qt::Sheet);
+
+ connect(
+ renameTree, SIGNAL(itemClicked(QTreeWidgetItem *, int)),
+ this, SLOT(itemClicked(QTreeWidgetItem *, int))
+ );
+
+ renameTree->setHeaderLabels(QStringList()
+ << tr("Missing paths and possible rename targets")
+ << tr("File ID (if applicable)")
+ );
+
+ renameTree->header()->resizeSection(0, 300);
+
+ QFont boldFont;
+ boldFont.setBold(true);
+ IconProvider * iconProvider = IconProvider::singleton();
+
+ QMapIterator i(renames);
+ while (i.hasNext())
+ {
+ i.next();
+ FileEntry sen = i.key();
+ FileEntryList list = i.value();
+ QTreeWidgetItem * source = new QTreeWidgetItem(renameTree);
+ source->setFlags(Qt::ItemIsEnabled);
+ source->setText(0, QString("%1 (%2)").arg(sen.path).arg(list.size()));
+ source->setData(0, Qt::UserRole, QVariant(sen.path));
+ source->setFont(0, boldFont);
+ source->setIcon(0, sen.is_dir ?
+ iconProvider->getPlainFolderIcon() :
+ iconProvider->getPlainFileIcon());
+ source->setText(1, sen.fileid.isEmpty() ? tr("n/a") : sen.fileid);
+ source->setFont(1, boldFont);
+
+ foreach (FileEntry en, list)
+ {
+ QTreeWidgetItem * target = new QTreeWidgetItem(source);
+ target->setFlags(Qt::ItemIsEnabled | Qt::ItemIsUserCheckable);
+ target->setCheckState(0, Qt::Unchecked);
+ target->setText(0, en.path);
+ target->setToolTip(0, en.fileid.isEmpty() || sen.fileid != en.fileid ?
+ tr("matched by name") :
+ tr("matched by content"));
+
+ target->setText(1, en.fileid.isEmpty() ? tr("n/a") : en.fileid);
+ }
+ }
+}
+
+UnaccountedRenames::~UnaccountedRenames()
+{
+}
+
+void UnaccountedRenames::itemClicked(QTreeWidgetItem * item, int column)
+{
+ if (item == 0 || column == -1) return;
+ QTreeWidgetItem * parent = item->parent();
+ // top level item clicked
+ if (!parent) return;
+
+ Qt::CheckState state = item->checkState(0);
+
+ for (int i=0, j=parent->childCount(); ichild(i)->setCheckState(0, Qt::Unchecked);
+ }
+
+ item->setCheckState(0, state);
+}
+
============================================================
--- src/view/dialogs/UnaccountedRenames.h 0691bf54c8ff2d8606e88eb855613a4977ad8b9d
+++ src/view/dialogs/UnaccountedRenames.h 0691bf54c8ff2d8606e88eb855613a4977ad8b9d
@@ -0,0 +1,43 @@
+/***************************************************************************
+ * Copyright (C) 2007 by Thomas Keller *
+ * address@hidden *
+ * *
+ * This program is free software; you can redistribute it and/or modify *
+ * it under the terms of the GNU General Public License as published by *
+ * the Free Software Foundation; either version 2 of the License, or *
+ * (at your option) any later version. *
+ * *
+ * This program is distributed in the hope that it will be useful, *
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of *
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
+ * GNU General Public License for more details. *
+ * *
+ * You should have received a copy of the GNU General Public License *
+ * along with this program; if not, write to the *
+ * Free Software Foundation, Inc., *
+ * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
+ ***************************************************************************/
+
+#ifndef UNACCOUNTED_RENAMES_H
+#define UNACCOUNTED_RENAMES_H
+
+#include "Dialog.h"
+#include "vocab.h"
+#include "ui_unaccounted_renames.h"
+
+class UnaccountedRenames : public Dialog, private Ui::UnaccountedRenamesDialog
+{
+ Q_OBJECT
+
+public:
+ UnaccountedRenames(QWidget *, const QMap &);
+ ~UnaccountedRenames();
+
+private:
+ QMap renames;
+
+private slots:
+ void itemClicked(QTreeWidgetItem *, int);
+};
+
+#endif
============================================================
--- guitone.pro 03f06140d2a1fe2ab82feb90a374d8546ab96ab6
+++ guitone.pro 8a3fa0c8ffda3b50a30fe54d2429a62f130b0f9c
@@ -36,6 +36,7 @@ HEADERS = src/view/MainWindow.h \
src/view/dialogs/RevisionManifest.h \
src/view/dialogs/CommitRevision.h \
src/view/dialogs/FileHistory.h \
+ src/view/dialogs/UnaccountedRenames.h \
src/monotone/Monotone.h \
src/monotone/MonotoneDelegate.h \
src/monotone/FileExporter.h \
@@ -96,6 +97,7 @@ SOURCES += src/view/MainWindow.cpp \
src/view/dialogs/RevisionManifest.cpp \
src/view/dialogs/CommitRevision.cpp \
src/view/dialogs/FileHistory.cpp \
+ src/view/dialogs/UnaccountedRenames.cpp \
src/monotone/Monotone.cpp \
src/monotone/MonotoneDelegate.cpp \
src/monotone/FileExporter.cpp \
@@ -149,7 +151,8 @@ FORMS += res/forms/select_revision.ui
res/forms/manifest.ui \
res/forms/commit_revision.ui \
res/forms/application_update.ui \
- res/forms/file_history.ui
+ res/forms/file_history.ui \
+ res/forms/unaccounted_renames.ui
UI_DIR = tmp
============================================================
--- res/forms/main_window.ui 9f9f41d6a2d521f10475aabdd7e2901b29243dd1
+++ res/forms/main_window.ui 600472d44fd5cd51100da93909a592d1815e3505
@@ -187,6 +187,7 @@
+
@@ -459,14 +460,14 @@
Ctrl+R
+
+
+ Find unaccounted renames
+
+
- Splitter
- QSplitter
-
-
-
InventoryView
QTreeView
@@ -476,6 +477,11 @@
QTreeView
+
+ Splitter
+ QSplitter
+
+
============================================================
--- src/model/Inventory.cpp 1e554a233746a80cf81f5bac48efd24eeca7b873
+++ src/model/Inventory.cpp 3cceb0b22340f61c7beba6c6d2e05392d1839cc2
@@ -439,19 +439,22 @@ bool Inventory::handleError(int errCode)
return true;
}
-QMap Inventory::findUnaccountedRenames()
+QMap Inventory::findUnaccountedRenames()
{
QList missingItems;
+ QList unknownItems;
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))
+ if (item->hasStatus(InventoryItem::Missing))
{
missingItems.append(item);
}
+
+ if (item->hasStatus(InventoryItem::Unknown))
+ {
+ unknownItems.append(item);
+ }
}
// TODO: progess bar here!
@@ -460,34 +463,56 @@ QMap Inventory::
FileEntryList parentList =
MonotoneDelegate::getRevisionManifest(this, parentRev);
- QMap unaccountedRenames;
+ QMap unaccountedRenames;
+ QMap fileIds;
- foreach (InventoryItem * item, missingItems)
+ foreach (InventoryItem * missingItem, missingItems)
{
FileEntryList candidates;
+ FileEntry entry;
+ // this is a new entry not recorded in the base roster
+ if (missingItem->hasStatus(InventoryItem::Added))
+ {
+ entry.path = missingItem->getPath();
+ entry.is_dir = missingItem->isDirectory();
+ }
+ else
+ {
+ bool found = false;
+ foreach (entry, parentList)
+ {
+ if (entry.path == missingItem->getPath())
+ {
+ found = true;
+ break;
+ }
+ }
+ I(found);
+ }
+
// at first check if the simple case: the item was renamed
// in the bookkeeping dir, but not physically
- if (item->hasStatus(InventoryItem::RenamedTo))
+ if (missingItem->hasStatus(InventoryItem::RenamedTo))
{
- InventoryItem * oldItem = item->getRenamedFrom();
+ InventoryItem * oldItem = missingItem->getRenamedFrom();
I(oldItem);
// yes, there is an unknown path with the same name!
if (oldItem->hasStatus(InventoryItem::Unknown))
{
bool found = false;
- FileEntry entry;
- foreach (entry, parentList)
+ FileEntry oldEntry;
+ foreach (oldEntry, parentList)
{
- if (entry.path == oldItem->getPath())
+ if (oldEntry.path == oldItem->getPath())
{
found = true;
break;
}
}
I(found);
- candidates.append(entry);
+ candidates.append(oldEntry);
}
else
{
@@ -496,31 +521,54 @@ QMap Inventory::
}
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)
+ foreach (InventoryItem * unknownItem, unknownItems)
{
- if (entry.path == item->getPath())
+ QString unknownPath = unknownItem->getPath();
+
+ // calculate the file id of the unknown file
+ if (!unknownItem->isDirectory() &&
+ !fileIds.contains(unknownPath))
{
- found = true;
- break;
+ QString fileid = MonotoneDelegate::getFileId(this, unknownPath);
+ // file was not readable, etc.
+ if (fileid.isEmpty()) continue;
+ fileIds.insert(unknownPath, fileid);
}
+
+ // at first do a simple file name check
+ if ((missingItem->getFilename() == unknownItem->getFilename()) &&
+ (missingItem->isDirectory() == unknownItem->isDirectory()))
+ {
+ FileEntry can(unknownPath, unknownItem->isDirectory());
+ if (fileIds.contains(unknownPath))
+ {
+ can.fileid = fileIds.value(unknownPath);
+ }
+ candidates.append(can);
+ continue;
+ }
+
+ // we can't do anything for directories from here on
+ if (missingItem->isDirectory() || unknownItem->isDirectory())
+ continue;
+
+ D(unknownPath);
+ // we now rely on the fact that we have a fileid
+ I(fileIds.contains(unknownPath));
+
+ if (fileIds.value(unknownPath) == entry.fileid)
+ {
+ candidates.append(FileEntry(unknownPath, false, entry.fileid));
+ }
}
- I(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);
+ // only add those missing items to the map which are having any
+ // candidates, i.e. are likely to be renamed
+ if (candidates.size() > 0)
+ {
+ unaccountedRenames.insert(entry, candidates);
+ }
}
return unaccountedRenames;
============================================================
--- src/model/Inventory.h cb7bd39962c8f983a04c7410318e1643fb048ede
+++ src/model/Inventory.h c9834d77c69b3b9d7c6dc35a5a8bc6118a5b01ec
@@ -37,7 +37,7 @@ class Inventory : public QAbstractItemMo
Inventory(QObject *parent);
~Inventory();
bool readInventory();
- QMap findUnaccountedRenames();
+ QMap findUnaccountedRenames();
// needed Qt Model methods
QVariant data(const QModelIndex&, int) const;
============================================================
--- src/monotone/Monotone.cpp 0fe70345373d6aa6338a8a237f7d0ddda2a2d308
+++ src/monotone/Monotone.cpp e064e50d6f2e22d1c15cd325346311a4ab93e97c
@@ -89,8 +89,8 @@
// - if not, we additionally check the interface version in case the user
// compiled his own version with all needed commands
//
-const QString Monotone::RequiredProgramVersion = "0.34";
-const QString Monotone::RequiredInterfaceVersion = "4.1";
+const QString Monotone::RequiredProgramVersion = "0.35";
+const QString Monotone::RequiredInterfaceVersion = "4.2";
const int Monotone::StdioBufferSize = 50 * 1024 * 1024;
const int Monotone::WaitForMonotoneStart = 15000; // milliseconds
============================================================
--- src/monotone/MonotoneDelegate.cpp 6fe7d984293395f5d83ffdcf0c6735d93186a6f7
+++ src/monotone/MonotoneDelegate.cpp 0b1b0ebfaa468cbf89d3ef32a54e9791405b86f5
@@ -393,3 +393,23 @@ QStringList MonotoneDelegate::getPrivate
}
return keys;
}
+
+QString MonotoneDelegate::getFileId(QObject * obj, const QString & path)
+{
+ Monotone * mtn = MTN(obj);
+
+ int cmdNum;
+ mtn->executeCommand(QStringList() << "identify" << path, cmdNum);
+
+ QString data = mtn->getDecodedData(cmdNum);
+
+ if (mtn->getReturnCode(cmdNum) > 0)
+ {
+ C(QString("Couldn't identify path: %1").arg(data));
+ return QString();
+ }
+
+ data.chop(1);
+ return data;
+}
+
============================================================
--- src/monotone/MonotoneDelegate.h 4a188eda03b4f743f508cdd05a333102dfeb0337
+++ src/monotone/MonotoneDelegate.h 6971e5c0d79d50fc766f84fb395cf648da80c8e5
@@ -47,6 +47,7 @@ public:
static QString getDatabaseFilePath(QObject *);
static FileEntryList getRevisionManifest(QObject *, const QString & rev = QString());
static QStringList getPrivateKeyList(QObject *);
+ static QString getFileId(QObject *, const QString &);
private:
AutomateCommand * cmdModel;
============================================================
--- src/view/MainWindow.cpp a12fb6d3645a244dadb84a4e44c95bf2f203dae6
+++ src/view/MainWindow.cpp 7064dcc81b11c6991521f64da0762beb9a347350
@@ -38,6 +38,7 @@
#include "Settings.h"
#include "ChangesetBrowser.h"
#include "WorkspaceCreator.h"
+#include "UnaccountedRenames.h"
#include "Guitone.h"
#ifdef Q_WS_MAC
@@ -659,3 +660,21 @@ void MainWindow::on_actionReload_workspa
if (!ret) C("Could not read inventory.");
}
+void MainWindow::on_actionFind_unaccounted_renames_triggered()
+{
+ QMap renames = invModel->findUnaccountedRenames();
+ if (renames.size() == 0)
+ {
+ QMessageBox::information(
+ this,
+ tr("Nothing found"),
+ tr("No unaccounted renames found for this workspace."),
+ QMessageBox::Ok
+ );
+ return;
+ }
+
+ UnaccountedRenames dlg(this, renames);
+ dlg.execDocumentModal();
+}
+
============================================================
--- src/view/MainWindow.h 38296ee6b6bbd5e84836949b27167abccb20bb6a
+++ src/view/MainWindow.h ed423769deea41872ede542b5920a765f790a38a
@@ -75,6 +75,7 @@ private slots:
void on_actionBring_all_to_front_triggered();
void on_actionCheck_for_updates_triggered();
void on_actionReload_workspace_triggered();
+ void on_actionFind_unaccounted_renames_triggered();
void openRecentWorkspace();
void openRecentDatabase();
============================================================
--- src/view/dialogs/FileHistory.cpp fb8b038667a771cbaee11c0f60d1269104e774d3
+++ src/view/dialogs/FileHistory.cpp 47ab8b46f70a4e81ecadd0c686f00dc8874c3fde
@@ -1,5 +1,5 @@
/***************************************************************************
- * Copyright (C) 2006 by Thomas Keller *
+ * Copyright (C) 2007 by Thomas Keller *
* address@hidden *
* *
* This program is free software; you can redistribute it and/or modify *
============================================================
--- src/vocab.h 65c921759f5565377c8ffe49fb8abbfbb43fe27c
+++ src/vocab.h ed37b5f6deab84d3025ff1bfb2b0341993391751
@@ -46,6 +46,11 @@ struct FileEntry {
bool is_dir;
QString fileid;
QMap attrs;
+
+ inline bool operator<(const FileEntry & other) const
+ {
+ return path < other.path;
+ }
};
typedef QList FileEntryList;