Mercurial > hg > easyhg
changeset 163:5c262ac73948
* First cut of work on merge/resolve logic
author | Chris Cannam |
---|---|
date | Fri, 03 Dec 2010 19:35:04 +0000 |
parents | 910c2c5d1873 |
children | de39da2f9f4d |
files | filestates.cpp filestates.h filestatuswidget.cpp filestatuswidget.h hgaction.h hgtabwidget.cpp hgtabwidget.h mainwindow.cpp mainwindow.h |
diffstat | 9 files changed, 251 insertions(+), 91 deletions(-) [+] |
line wrap: on
line diff
--- 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()
--- 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);
--- 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.<br>" "If you intended to delete them, select them and use Remove to tell the version control system about it.<br>" "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.<br>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.<br>" "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; }
--- 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();
--- 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,
--- 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);
--- 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();
--- 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("<h3>%1</h3><p>%2").arg(rf) .arg(tr("You are about to <b>revert</b> %n file(s).<br><br>This will <b>throw away any changes</b> 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("<qt><h3>Merge resolved</h3><p>Merge resolved successfully.</p>")); shouldHgStat = true; justMerged = true; break; @@ -1368,11 +1456,11 @@ } // Sequence when no full log required: - // paths -> branch -> stat -> heads -> + // paths -> branch -> stat -> resolve-list -> heads -> // incremental-log (only if heads changed) -> parents // // Sequence when full log required: - // paths -> branch -> stat -> heads -> parents -> log + // paths -> branch -> stat -> resolve-list -> heads -> parents -> log // // Note we want to call enableDisableActions only once, at the end // of whichever sequence is in use. @@ -1390,6 +1478,10 @@ break; case ACT_STAT: + hgResolveList(); + break; + + case ACT_RESOLVE_LIST: hgQueryHeads(); break; @@ -1420,6 +1512,7 @@ default: if (shouldHgStat) { + shouldHgStat = false; hgQueryPaths(); } else { noMore = true; @@ -1448,7 +1541,6 @@ connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate())); connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert())); connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge())); - connect(hgRetryMergeAct, SIGNAL(triggered()), this, SLOT(hgRetryMerge())); connect(hgTagAct, SIGNAL(triggered()), this, SLOT(hgTag())); connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore())); @@ -1465,8 +1557,6 @@ // connect(hgUpdateToRevAct, SIGNAL(triggered()), this, SLOT(hgUpdateToRev())); connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate())); - connect(hgResolveListAct, SIGNAL(triggered()), this, SLOT(hgResolveList())); - connect(hgResolveMarkAct, SIGNAL(triggered()), this, SLOT(hgResolveMark())); connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe())); connect(clearSelectionsAct, SIGNAL(triggered()), this, SLOT(clearSelections())); } @@ -1562,9 +1652,6 @@ hgUpdateAct -> setEnabled(localRepoActionsEnabled); hgCommitAct -> setEnabled(localRepoActionsEnabled); hgMergeAct -> setEnabled(localRepoActionsEnabled); - hgRetryMergeAct -> setEnabled(localRepoActionsEnabled); - hgResolveListAct -> setEnabled(localRepoActionsEnabled); - hgResolveMarkAct -> setEnabled(localRepoActionsEnabled); hgAnnotateAct -> setEnabled(localRepoActionsEnabled); hgServeAct -> setEnabled(localRepoActionsEnabled); hgTagAct -> setEnabled(localRepoActionsEnabled); @@ -1579,8 +1666,8 @@ hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd()); hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove()); hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); - hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); - hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDoDiff()); + hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canRevert()); + hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDiff()); // A default merge makes sense if: // * there is only one parent (if there are two, we have an uncommitted merge) and @@ -1624,10 +1711,13 @@ emptyRepo = true; } else { haveMerge = true; + justMerged = true; } - hgMergeAct->setEnabled(localRepoActionsEnabled && canMerge); - hgUpdateAct->setEnabled(localRepoActionsEnabled && canUpdate); + hgMergeAct->setEnabled(localRepoActionsEnabled && + (canMerge || hgTabs->canResolve())); + hgUpdateAct->setEnabled(localRepoActionsEnabled && + (canUpdate && !hgTabs->canRevert())); // Set the state field on the file status widget @@ -1644,10 +1734,18 @@ hgTabs->setState(tr("Nothing committed to this repository yet")); } else if (canMerge) { hgTabs->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText)); + } else if (!hgTabs->getAllUnresolvedFiles().empty()) { + hgTabs->setState(tr("Have unresolved files following merge on %1").arg(branchText)); } else if (haveMerge) { hgTabs->setState(tr("Have merged but not yet committed on %1").arg(branchText)); } else if (canUpdate) { - hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText)); + if (hgTabs->canRevert()) { + // have uncommitted changes + hgTabs->setState(tr("On %1. Not at the head of the branch").arg(branchText)); + } else { + // no uncommitted changes + hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText)); + } } else if (currentBranchHeads > 1) { hgTabs->setState(tr("At one of %n heads of %1", "", currentBranchHeads).arg(branchText)); } else { @@ -1722,15 +1820,6 @@ hgAnnotateAct = new QAction(tr("Annotate"), this); hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file")); - hgResolveListAct = new QAction(tr("Resolve (list)"), this); - hgResolveListAct -> setStatusTip(tr("Resolve (list): Show list of files needing merge")); - - hgResolveMarkAct = new QAction(tr("Resolve (mark)"), this); - hgResolveMarkAct -> setStatusTip(tr("Resolve (mark): Mark selected file status as resolved")); - - hgRetryMergeAct = new QAction(tr("Retry merge"), this); - hgRetryMergeAct -> setStatusTip(tr("Retry merge after failed merge attempt.")); - hgTagAct = new QAction(tr("Tag revision"), this); hgTagAct -> setStatusTip(tr("Give decsriptive name (tag) to current workfolder parent revision."));
--- a/mainwindow.h Fri Dec 03 14:43:32 2010 +0000 +++ b/mainwindow.h Fri Dec 03 19:35:04 2010 +0000 @@ -80,6 +80,7 @@ void hgUpdate(); void hgRevert(); void hgMerge(); + void hgMarkResolved(QStringList); void hgRetryMerge(); void hgCloneFromRemote(); void hgInit(); @@ -90,7 +91,6 @@ void hgMergeFrom(QString); void hgAnnotate(); void hgResolveList(); - void hgResolveMark(); void hgTag(); void hgServe(); void hgIgnore(); @@ -172,11 +172,8 @@ QAction *hgUpdateAct; QAction *hgCommitAct; QAction *hgMergeAct; - QAction *hgRetryMergeAct; QAction *hgUpdateToRevAct; QAction *hgAnnotateAct; - QAction *hgResolveListAct; - QAction *hgResolveMarkAct; QAction *hgTagAct; QAction *hgIgnoreAct; QAction *hgServeAct; @@ -199,12 +196,21 @@ HgRunner *runner; + bool shouldHgStat; + + QString diffBinaryName; + QString mergeBinaryName; + void findDiffBinaryName(); - QString diffBinaryName; + void findMergeBinaryName(); QFileSystemWatcher *fsWatcher; + QString lastStatOutput; + QStringList lastRevertedFiles; + bool justMerged; + QString mergeTargetRevision; QString mergeCommitComment; };