# HG changeset patch
# User Chris Cannam
# Date 1291404904 0
# Node ID 5c262ac7394811ae305ed0df7b8aaeb4bbf6a692
# Parent 910c2c5d1873cf8df7b11da8c2a8f39e588ea1d8
* First cut of work on merge/resolve logic
diff -r 910c2c5d1873 -r 5c262ac73948 filestates.cpp
--- a/filestates.cpp Fri Dec 03 14:43:32 2010 +0000
+++ b/filestates.cpp Fri Dec 03 19:35:04 2010 +0000
@@ -38,17 +38,19 @@
FileStates::State FileStates::charToState(QChar c, bool *ok)
{
// Note that InConflict does not correspond to a stat char -- it's
- // reported separately, by resolve --list -- stat reports files in
- // conflict as M which means they will appear in more than one bin
- // if we handle them naively.
+ // reported separately, by resolve --list, which shows U for
+ // Unresolved -- stat reports files in conflict as M, which means
+ // they will appear in more than one bin if we handle them
+ // naively. 'u' is also used by stat as the command option for
+ // Unknown, but the stat output uses ? for these so there's no
+ // ambiguity in parsing.
- //!!! -- but InConflict isn't actually handled elsewhere, it's
- //!!! -- only a placeholder really at the moment
if (ok) *ok = true;
if (c == 'M') return Modified;
if (c == 'A') return Added;
if (c == 'R') return Removed;
if (c == '!') return Missing;
+ if (c == 'U') return InConflict;
if (c == '?') return Unknown;
if (c == 'C') return Clean;
if (ok) *ok = false;
@@ -73,6 +75,7 @@
text.replace("\r\n", "\n");
clearBuckets();
+ m_stateMap.clear();
QStringList lines = text.split("\n", QString::SkipEmptyParts);
@@ -88,9 +91,13 @@
if (!ok) continue;
QString file = line.right(line.length() - 2);
- QStringList *bucket = stateToBucket(s);
+ m_stateMap[file] = s;
+ }
+
+ foreach (QString file, m_stateMap.keys()) {
+
+ QStringList *bucket = stateToBucket(m_stateMap[file]);
bucket->push_back(file);
- m_stateMap[file] = s;
}
DEBUG << "FileStates: " << m_modified.size() << " modified, " << m_added.size()
diff -r 910c2c5d1873 -r 5c262ac73948 filestates.h
--- a/filestates.h Fri Dec 03 14:43:32 2010 +0000
+++ b/filestates.h Fri Dec 03 19:35:04 2010 +0000
@@ -29,16 +29,19 @@
enum State {
- Clean,
+ // These are in the order in which they want to be listed in
+ // the interface
+
Modified,
Added,
+ Removed,
+ InConflict,
+ Missing,
+ Clean,
Unknown,
- Removed,
- Missing,
- InConflict,
- FirstState = Clean,
- LastState = InConflict
+ FirstState = Modified,
+ LastState = Unknown
};
void parseStates(QString text);
diff -r 910c2c5d1873 -r 5c262ac73948 filestatuswidget.cpp
--- a/filestatuswidget.cpp Fri Dec 03 14:43:32 2010 +0000
+++ b/filestatuswidget.cpp Fri Dec 03 19:35:04 2010 +0000
@@ -70,6 +70,7 @@
m_simpleLabels[FileStates::Added] = tr("Added:");
m_simpleLabels[FileStates::Removed] = tr("Removed:");
m_simpleLabels[FileStates::Missing] = tr("Missing:");
+ m_simpleLabels[FileStates::InConflict] = tr("In Conflict:");
m_simpleLabels[FileStates::Unknown] = tr("Untracked:");
m_descriptions[FileStates::Clean] = tr("You have not changed these files.");
@@ -80,6 +81,7 @@
m_descriptions[FileStates::Missing] = tr("These files are recorded in the version control, but absent from your working folder.
"
"If you intended to delete them, select them and use Remove to tell the version control system about it.
"
"If you deleted them by accident, select them and use Revert to restore their previous contents.");
+ m_descriptions[FileStates::InConflict] = tr("These files are unresolved following an incomplete merge.
Select a file and use Merge to try to resolve the merge again.");
m_descriptions[FileStates::Unknown] = tr("These files are in your working folder but are not under version control.
"
"Select a file and use Add to place it under version control or Ignore to remove it from this list.");
@@ -87,7 +89,7 @@
"have appeared since your most recent commit or update.");
for (int i = int(FileStates::FirstState);
- i <= int(FileStates::LastState); ++i) {
+ i <= int(FileStates::LastState); ++i) {
FileStates::State s = FileStates::State(i);
@@ -222,6 +224,7 @@
case FileStates::Modified:
case FileStates::Removed:
case FileStates::Missing:
+ case FileStates::InConflict:
files.push_back(f);
break;
default: break;
@@ -237,6 +240,28 @@
files << m_fileStates.getFilesInState(FileStates::Added);
files << m_fileStates.getFilesInState(FileStates::Removed);
files << m_fileStates.getFilesInState(FileStates::Missing);
+ files << m_fileStates.getFilesInState(FileStates::InConflict);
+ return files;
+}
+
+QStringList FileStatusWidget::getSelectedUnresolvedFiles() const
+{
+ QStringList files;
+ foreach (QString f, m_selectedFiles) {
+ switch (m_fileStates.getStateOfFile(f)) {
+ case FileStates::InConflict:
+ files.push_back(f);
+ break;
+ default: break;
+ }
+ }
+ return files;
+}
+
+QStringList FileStatusWidget::getAllUnresolvedFiles() const
+{
+ QStringList files;
+ files << m_fileStates.getFilesInState(FileStates::InConflict);
return files;
}
@@ -272,6 +297,7 @@
case FileStates::Added:
case FileStates::Modified:
case FileStates::Missing:
+ case FileStates::InConflict:
files.push_back(f);
break;
default: break;
@@ -287,6 +313,7 @@
files << m_fileStates.getFilesInState(FileStates::Added);
files << m_fileStates.getFilesInState(FileStates::Modified);
files << m_fileStates.getFilesInState(FileStates::Missing);
+ files << m_fileStates.getFilesInState(FileStates::InConflict);
return files;
}
diff -r 910c2c5d1873 -r 5c262ac73948 filestatuswidget.h
--- a/filestatuswidget.h Fri Dec 03 14:43:32 2010 +0000
+++ b/filestatuswidget.h Fri Dec 03 19:35:04 2010 +0000
@@ -64,6 +64,9 @@
QStringList getSelectedRemovableFiles() const;
QStringList getAllRemovableFiles() const;
+ QStringList getSelectedUnresolvedFiles() const;
+ QStringList getAllUnresolvedFiles() const;
+
signals:
void selectionChanged();
diff -r 910c2c5d1873 -r 5c262ac73948 hgaction.h
--- a/hgaction.h Fri Dec 03 14:43:32 2010 +0000
+++ b/hgaction.h Fri Dec 03 19:35:04 2010 +0000
@@ -27,6 +27,7 @@
ACT_QUERY_PATHS,
ACT_QUERY_BRANCH,
ACT_STAT,
+ ACT_RESOLVE_LIST,
ACT_QUERY_HEADS,
ACT_QUERY_PARENTS,
ACT_LOG,
@@ -45,7 +46,6 @@
ACT_UPDATE,
ACT_REVERT,
ACT_MERGE,
- ACT_RESOLVE_LIST,
ACT_SERVE,
ACT_RESOLVE_MARK,
ACT_RETRY_MERGE,
diff -r 910c2c5d1873 -r 5c262ac73948 hgtabwidget.cpp
--- a/hgtabwidget.cpp Fri Dec 03 14:43:32 2010 +0000
+++ b/hgtabwidget.cpp Fri Dec 03 19:35:04 2010 +0000
@@ -78,7 +78,9 @@
void HgTabWidget::setCurrent(QStringList ids, QString branch)
{
- m_historyWidget->setCurrent(ids, branch, canCommit());
+ bool showUncommitted = false;
+ if (canRevert()) showUncommitted = true;
+ m_historyWidget->setCurrent(ids, branch, showUncommitted);
}
void HgTabWidget::updateHistory()
@@ -86,16 +88,25 @@
m_historyWidget->update();
}
+bool HgTabWidget::canDiff() const
+{
+ if (!m_fileStatusWidget->getSelectedAddableFiles().empty()) return false;
+ return m_fileStatusWidget->haveChangesToCommit() ||
+ !m_fileStatusWidget->getAllUnresolvedFiles().empty();
+}
+
bool HgTabWidget::canCommit() const
{
if (!m_fileStatusWidget->getSelectedAddableFiles().empty()) return false;
- return m_fileStatusWidget->haveChangesToCommit();
+ return m_fileStatusWidget->haveChangesToCommit() &&
+ m_fileStatusWidget->getAllUnresolvedFiles().empty();
}
bool HgTabWidget::canRevert() const
{
+ if (!m_fileStatusWidget->getSelectedAddableFiles().empty()) return false;
return m_fileStatusWidget->haveChangesToCommit() ||
- !m_fileStatusWidget->getSelectedRevertableFiles().empty();
+ !m_fileStatusWidget->getAllUnresolvedFiles().empty();
}
bool HgTabWidget::canAdd() const
@@ -113,9 +124,9 @@
return true;
}
-bool HgTabWidget::canDoDiff() const
+bool HgTabWidget::canResolve() const
{
- return canCommit();
+ return !m_fileStatusWidget->getSelectedUnresolvedFiles().empty();
}
QStringList HgTabWidget::getAllSelectedFiles() const
@@ -158,6 +169,16 @@
return m_fileStatusWidget->getSelectedRemovableFiles();
}
+QStringList HgTabWidget::getAllUnresolvedFiles() const
+{
+ return m_fileStatusWidget->getAllUnresolvedFiles();
+}
+
+QStringList HgTabWidget::getSelectedUnresolvedFiles() const
+{
+ return m_fileStatusWidget->getSelectedUnresolvedFiles();
+}
+
void HgTabWidget::updateWorkFolderFileList(QString fileList)
{
m_fileStates.parseStates(fileList);
diff -r 910c2c5d1873 -r 5c262ac73948 hgtabwidget.h
--- a/hgtabwidget.h Fri Dec 03 14:43:32 2010 +0000
+++ b/hgtabwidget.h Fri Dec 03 19:35:04 2010 +0000
@@ -54,11 +54,12 @@
FileStates getFileStates() { return m_fileStates; }
+ bool canDiff() const;
bool canCommit() const;
bool canRevert() const;
bool canAdd() const;
bool canRemove() const;
- bool canDoDiff() const;
+ bool canResolve() const;
QStringList getAllSelectedFiles() const;
@@ -74,6 +75,9 @@
QStringList getSelectedRemovableFiles() const;
QStringList getAllRemovableFiles() const;
+ QStringList getSelectedUnresolvedFiles() const;
+ QStringList getAllUnresolvedFiles() const;
+
signals:
void selectionChanged();
diff -r 910c2c5d1873 -r 5c262ac73948 mainwindow.cpp
--- a/mainwindow.cpp Fri Dec 03 14:43:32 2010 +0000
+++ b/mainwindow.cpp Fri Dec 03 19:35:04 2010 +0000
@@ -47,6 +47,7 @@
fsWatcher = 0;
commitsSincePush = 0;
+ shouldHgStat = true;
createActions();
createMenus();
@@ -85,6 +86,7 @@
}
findDiffBinaryName();
+ findMergeBinaryName();
ColourSet *cs = ColourSet::instance();
cs->clearDefaultNames();
@@ -161,6 +163,8 @@
QStringList params;
params << "stat" << "-ardum";
+ lastStatOutput = "";
+
// annoyingly, hg stat actually modifies the working directory --
// it creates files called hg-checklink and hg-checkexec to test
// properties of the filesystem
@@ -240,19 +244,6 @@
}
}
-void MainWindow::hgResolveMark()
-{
- QStringList params;
- QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
-
- if (!currentFile.isEmpty())
- {
- params << "resolve" << "--mark" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
-
- runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
- }
-}
-
void MainWindow::hgResolveList()
{
QStringList params;
@@ -331,8 +322,6 @@
runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params));
}
-
- justMerged = false;
}
QString MainWindow::filterTag(QString tag)
@@ -424,6 +413,30 @@
diffBinaryName = diff;
}
+void MainWindow::findMergeBinaryName()
+{
+ QSettings settings;
+ QString merge = settings.value("mergebinary", "").toString();
+ if (merge == "") {
+ QStringList bases;
+ bases << "fmdiff3" << "kdiff3" << "meld" << "diffuse";
+ bool found = false;
+ foreach (QString base, bases) {
+ merge = findExecutable(base);
+ if (merge != base) {
+ found = true;
+ break;
+ }
+ }
+ if (found) {
+ settings.setValue("mergebinary", merge);
+ } else {
+ merge = "";
+ }
+ }
+ mergeBinaryName = merge;
+}
+
void MainWindow::hgFolderDiff()
{
if (diffBinaryName == "") return;
@@ -443,6 +456,8 @@
void MainWindow::hgDiffToCurrent(QString id)
{
+ if (diffBinaryName == "") return;
+
QStringList params;
// Diff given revision against working folder
@@ -457,6 +472,8 @@
void MainWindow::hgDiffToParent(QString child, QString parent)
{
+ if (diffBinaryName == "") return;
+
QStringList params;
// Diff given revision against working folder
@@ -508,32 +525,92 @@
tr("
%2").arg(rf)
.arg(tr("You are about to revert %n file(s). Merge resolved successfully.
This will throw away any changes that you have made to these files but have not committed.", "", files.size())),
files)) {
+
+ lastRevertedFiles = files;
if (files.empty()) {
params << "revert" << "--no-backup";
} else {
params << "revert" << "--no-backup" << "--" << files;
}
+
+ //!!! This is problematic. If you've got an uncommitted
+ //!!! merge, you can't revert it without declaring which
+ //!!! parent of the merge you want to revert to (reasonably
+ //!!! enough). We're OK if we just did the merge in easyhg a
+ //!!! moment ago, because we have a record of which parent was
+ //!!! the target -- but if you exit and restart, we've lost
+ //!!! that record and it doesn't appear to be possible to get
+ //!!! it back from Hg. Even if you just switched from one
+ //!!! repo to another, the record is lost. What to do?
+
+ if (justMerged && mergeTargetRevision != "") {
+ params << "--rev" << mergeTargetRevision;
+ }
runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params));
}
}
+
+void MainWindow::hgMarkResolved(QStringList files)
+{
+ QStringList params;
+
+ params << "resolve" << "--mark";
+
+ if (files.empty()) {
+ params << "--all";
+ } else {
+ params << files;
+ }
+
+ runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
+}
+
+
void MainWindow::hgRetryMerge()
{
QStringList params;
- params << "resolve" << "--all";
+ params << "resolve";
+
+ if (mergeBinaryName != "") {
+ params << "--tool" << mergeBinaryName;
+ }
+
+ QStringList files = hgTabs->getSelectedUnresolvedFiles();
+ if (files.empty()) {
+ params << "--all";
+ } else {
+ params << files;
+ }
+
runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params));
+
+ mergeCommitComment = tr("Merge");
}
void MainWindow::hgMerge()
{
+ if (hgTabs->canResolve()) {
+ hgRetryMerge();
+ return;
+ }
+
QStringList params;
params << "merge";
-
+
+ if (mergeBinaryName != "") {
+ params << "--tool" << mergeBinaryName;
+ }
+
+ if (currentParents.size() == 1) {
+ mergeTargetRevision = currentParents[0]->id();
+ }
+
runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
mergeCommitComment = tr("Merge");
@@ -546,6 +623,10 @@
params << "merge";
params << "--rev" << Changeset::hashOf(id);
+
+ if (mergeBinaryName != "") {
+ params << "--tool" << mergeBinaryName;
+ }
runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
@@ -673,6 +754,10 @@
foreach (Changeset *cs, currentHeads) delete cs;
currentHeads.clear();
currentBranch = "";
+ lastStatOutput = "";
+ lastRevertedFiles.clear();
+ mergeTargetRevision = "";
+ mergeCommitComment = "";
needNewLog = true;
}
@@ -1198,25 +1283,6 @@
: "");
QMessageBox::warning(this, tr("Command failed"), message);
-
-/* todo:
-if ((runningAction == ACT_MERGE) && (exitCode != 0))
- {
- // If we had a failed merge, offer to retry
- if (QMessageBox::Ok == QMessageBox::information(this, tr("Retry merge ?"), tr("Merge attempt failed. retry ?"), QMessageBox::Ok | QMessageBox::Cancel))
- {
- runningAction = ACT_NONE;
- hgRetryMerge();
- }
- else
- {
- runningAction = ACT_NONE;
- hgStat();
- }
- }
- else
- {
-*/
}
void MainWindow::commandCompleted(HgAction completedAction, QString output)
@@ -1225,7 +1291,6 @@
if (action == ACT_NONE) return;
- bool shouldHgStat = false;
bool headsChanged = false;
QStringList oldHeadIds;
@@ -1253,17 +1318,35 @@
case ACT_STAT:
if (fsWatcher) fsWatcher->blockSignals(false);
- hgTabs->updateWorkFolderFileList(output);
+ lastStatOutput = output;
updateFileSystemWatcher();
break;
+
+ case ACT_RESOLVE_LIST:
+ if (output != "") {
+ // Remove lines beginning with R (they are resolved,
+ // and the file stat parser treats R as removed)
+ QStringList outList = output.split('\n');
+ QStringList winnowed;
+ foreach (QString line, outList) {
+ if (!line.startsWith("R ")) winnowed.push_back(line);
+ }
+ output = winnowed.join("\n");
+ }
+ DEBUG << "lastStatOutput = " << lastStatOutput << endl;
+ DEBUG << "output = " << output << endl;
+ hgTabs->updateWorkFolderFileList(lastStatOutput + output);
+ break;
+
+ case ACT_RESOLVE_MARK:
+ shouldHgStat = true;
+ break;
case ACT_INCOMING:
showIncoming(output);
break;
case ACT_ANNOTATE:
- case ACT_RESOLVE_LIST:
- case ACT_RESOLVE_MARK:
presentLongStdoutToUser(output);
shouldHgStat = true;
break;
@@ -1327,12 +1410,17 @@
case ACT_COMMIT:
hgTabs->clearSelections();
+ justMerged = false;
shouldHgStat = true;
break;
+
+ case ACT_REVERT:
+ hgMarkResolved(lastRevertedFiles);
+ justMerged = false;
+ break;
case ACT_REMOVE:
case ACT_ADD:
- case ACT_REVERT:
hgTabs->clearSelections();
shouldHgStat = true;
break;
@@ -1357,8 +1445,8 @@
break;
case ACT_RETRY_MERGE:
- QMessageBox::information(this, tr("Merge retry"),
- tr("Merge retry successful."));
+ QMessageBox::information(this, tr("Resolved"),
+ tr("Merge resolved