comparison mainwindow.cpp @ 237:c9a7e4ec2f78

* Try to do the right thing when completely reverting a merge (forget that the merge took place) * When trying to clone from remote repo to existing local dir, offer to create a subdir instead * Tidy up clone-successful notification * Add a note to commit and revert confirmation dialogs to tell user if they are committing/reverting only a subset of available files
author Chris Cannam
date Mon, 10 Jan 2011 12:44:03 +0000
parents 2f4d401ce47c
children e2f2c6e3c01b
comparison
equal deleted inserted replaced
236:960b782f0a64 237:c9a7e4ec2f78
29 #include <QToolButton> 29 #include <QToolButton>
30 #include <QSettings> 30 #include <QSettings>
31 #include <QInputDialog> 31 #include <QInputDialog>
32 #include <QRegExp> 32 #include <QRegExp>
33 #include <QShortcut> 33 #include <QShortcut>
34 #include <QUrl>
34 35
35 #include "mainwindow.h" 36 #include "mainwindow.h"
36 #include "multichoicedialog.h" 37 #include "multichoicedialog.h"
37 #include "startupdialog.h" 38 #include "startupdialog.h"
38 #include "colourset.h" 39 #include "colourset.h"
389 { 390 {
390 QStringList params; 391 QStringList params;
391 392
392 QStringList files = hgTabs->getSelectedRemovableFiles(); 393 QStringList files = hgTabs->getSelectedRemovableFiles();
393 394
394 //!!! todo: confirmation dialog (with file list in it) (or do we
395 // need that? all it does is move the files to the removed
396 // list... doesn't it?)
397
398 if (!files.empty()) { 395 if (!files.empty()) {
399 params << "remove" << "--after" << "--force" << "--" << files; 396 params << "remove" << "--after" << "--force" << "--" << files;
400 runner->requestAction(HgAction(ACT_REMOVE, workFolderPath, params)); 397 runner->requestAction(HgAction(ACT_REMOVE, workFolderPath, params));
401 } 398 }
402 } 399 }
409 if (justMerged) { 406 if (justMerged) {
410 comment = mergeCommitComment; 407 comment = mergeCommitComment;
411 } 408 }
412 409
413 QStringList files = hgTabs->getSelectedCommittableFiles(); 410 QStringList files = hgTabs->getSelectedCommittableFiles();
411 QStringList allFiles = hgTabs->getAllCommittableFiles();
414 QStringList reportFiles = files; 412 QStringList reportFiles = files;
415 if (reportFiles.empty()) reportFiles = hgTabs->getAllCommittableFiles(); 413 if (reportFiles.empty()) {
416 414 reportFiles = allFiles;
415 }
416
417 QString subsetNote;
418 if (reportFiles != allFiles) {
419 subsetNote = tr("<p><b>Note:</b> you are committing only the files you have selected, not all of the files that have been changed!");
420 }
421
417 QString cf(tr("Commit files")); 422 QString cf(tr("Commit files"));
418 423
419 if (ConfirmCommentDialog::confirmAndGetLongComment 424 if (ConfirmCommentDialog::confirmAndGetLongComment
420 (this, 425 (this,
421 cf, 426 cf,
422 tr("<h3>%1</h3><p>%2").arg(cf) 427 tr("<h3>%1</h3><p>%2%3").arg(cf)
423 .arg(tr("You are about to commit the following files:")), 428 .arg(tr("You are about to commit the following files."))
424 tr("<h3>%1</h3><p>%2").arg(cf) 429 .arg(subsetNote),
425 .arg(tr("You are about to commit %n file(s).", "", reportFiles.size())), 430 tr("<h3>%1</h3><p>%2%3").arg(cf)
431 .arg(tr("You are about to commit %n file(s).", "", reportFiles.size()))
432 .arg(subsetNote),
426 reportFiles, 433 reportFiles,
427 comment, 434 comment,
428 tr("Commit"))) { 435 tr("Commit"))) {
429 436
430 if (!justMerged && !files.empty()) { 437 if (!justMerged && !files.empty()) {
536 543
537 QString MainWindow::findMergeBinaryName() 544 QString MainWindow::findMergeBinaryName()
538 { 545 {
539 QSettings settings; 546 QSettings settings;
540 settings.beginGroup("Locations"); 547 settings.beginGroup("Locations");
541 QVariant v = settings.value("mergebinary"); 548 if (settings.contains("mergebinary")) {
542 if (v != QVariant()) { 549 // use it even if empty: user may have specified no external tool
543 return v.toString(); // even if empty: user may have specified no external tool 550 QVariant v = settings.value("mergebinary");
551 DEBUG << "v = " << v << endl;
552 return v.toString();
544 } 553 }
545 QString merge; 554 QString merge;
546 QStringList bases; 555 QStringList bases;
547 #ifdef Q_OS_MAC 556 #ifdef Q_OS_MAC
548 bases << "easyhg-merge-osx.sh"; 557 bases << "easyhg-merge-osx.sh";
549 #endif 558 #endif
550 bases << "meld" << "diffuse" << "kdiff3"; 559 // I think this is too dangerous, given command line ordering
560 // differences and suchlike. Need to make sure the hg
561 // installation is configured OK instead
562 // bases << "meld" << "diffuse" << "kdiff3";
551 bool found = false; 563 bool found = false;
552 foreach (QString base, bases) { 564 foreach (QString base, bases) {
553 merge = findInPath(base, m_myDirPath, true); 565 merge = findInPath(base, m_myDirPath, true);
554 if (merge != "") { 566 if (merge != "") {
555 found = true; 567 found = true;
683 695
684 void MainWindow::hgRevert() 696 void MainWindow::hgRevert()
685 { 697 {
686 QStringList params; 698 QStringList params;
687 QString comment; 699 QString comment;
700 bool all = false;
688 701
689 QStringList files = hgTabs->getSelectedRevertableFiles(); 702 QStringList files = hgTabs->getSelectedRevertableFiles();
690 if (files.empty()) files = hgTabs->getAllRevertableFiles(); 703 QStringList allFiles = hgTabs->getAllRevertableFiles();
704 if (files.empty() || files == allFiles) {
705 files = allFiles;
706 all = true;
707 }
708
709 QString subsetNote;
710 if (!all) {
711 subsetNote = tr("<p><b>Note:</b> you are reverting only the files you have selected, not all of the files that have been changed!");
712 }
691 713
692 QString rf(tr("Revert files")); 714 QString rf(tr("Revert files"));
693 715
716 // Set up params before asking for confirmation, because there is
717 // a failure case here that we would need to report on early
718
719 DEBUG << "hgRevert: justMerged = " << justMerged << ", mergeTargetRevision = " << mergeTargetRevision << endl;
720
721 if (justMerged) {
722
723 // This is a little fiddly. The proper way to "revert" the
724 // whole of an uncommitted merge is with "hg update --clean ."
725 // But if the user has selected only some files, we're sort of
726 // promising to revert only those, which means we need to
727 // specify which parent to revert to. We can only do that if
728 // we have a record of it, which we do if you just did the
729 // merge from within easyhg but don't if you've exited and
730 // restarted, or changed repository, since then. Hmmm.
731
732 if (all) {
733 params << "update" << "--clean" << ".";
734 } else {
735 if (mergeTargetRevision != "") {
736 params << "revert" << "--rev"
737 << Changeset::hashOf(mergeTargetRevision)
738 << "--" << files;
739 } else {
740 QMessageBox::information
741 (this, tr("Unable to revert"),
742 tr("<qt><b>Sorry, unable to revert these files</b><br><br>EasyMercurial can only revert a subset of files during a merge if it still has a record of which parent was the original merge target; that information is no longer available.<br><br>This is a limitation of EasyMercurial. Consider reverting all files, or using hg revert with a specific revision at the command-line instead.</qt>"));
743 return;
744 }
745 }
746 } else {
747 params << "revert" << "--" << files;
748 }
749
694 if (ConfirmCommentDialog::confirmDangerousFilesAction 750 if (ConfirmCommentDialog::confirmDangerousFilesAction
695 (this, 751 (this,
696 rf, 752 rf,
697 tr("<h3>%1</h3><p>%2").arg(rf) 753 tr("<h3>%1</h3><p>%2%3").arg(rf)
698 .arg(tr("You are about to <b>revert</b> the following files to their previous committed state.<br><br>This will <b>throw away any changes</b> that you have made to these files but have not committed:")), 754 .arg(tr("You are about to <b>revert</b> the following files to their previous committed state.<br><br>This will <b>throw away any changes</b> that you have made to these files but have not committed."))
699 tr("<h3>%1</h3><p>%2").arg(rf) 755 .arg(subsetNote),
700 .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())), 756 tr("<h3>%1</h3><p>%2%3").arg(rf)
757 .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()))
758 .arg(subsetNote),
701 files, 759 files,
702 tr("Revert"))) { 760 tr("Revert"))) {
703 761
704 lastRevertedFiles = files; 762 lastRevertedFiles = files;
705
706 if (files.empty()) {
707 params << "revert" << "--no-backup";
708 } else {
709 params << "revert" << "--" << files;
710 }
711
712 //!!! This is problematic. If you've got an uncommitted
713 //!!! merge, you can't revert it without declaring which
714 //!!! parent of the merge you want to revert to (reasonably
715 //!!! enough). We're OK if we just did the merge in easyhg a
716 //!!! moment ago, because we have a record of which parent was
717 //!!! the target -- but if you exit and restart, we've lost
718 //!!! that record and it doesn't appear to be possible to get
719 //!!! it back from Hg. Even if you just switched from one
720 //!!! repo to another, the record is lost. What to do?
721
722 if (justMerged && mergeTargetRevision != "") {
723 params << "--rev" << mergeTargetRevision;
724 }
725 763
726 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params)); 764 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params));
727 } 765 }
728 } 766 }
729 767
760 params << "--all"; 798 params << "--all";
761 } else { 799 } else {
762 params << "--" << files; 800 params << "--" << files;
763 } 801 }
764 802
803 if (currentParents.size() == 1) {
804 mergeTargetRevision = currentParents[0]->id();
805 }
806
765 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params)); 807 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params));
766 808
767 mergeCommitComment = tr("Merge"); 809 mergeCommitComment = tr("Merge");
768 } 810 }
769 811
804 QString merge = findMergeBinaryName(); 846 QString merge = findMergeBinaryName();
805 if (merge != "") { 847 if (merge != "") {
806 params << "--tool" << merge; 848 params << "--tool" << merge;
807 } 849 }
808 850
851 if (currentParents.size() == 1) {
852 mergeTargetRevision = currentParents[0]->id();
853 }
854
809 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); 855 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
810 856
811 mergeCommitComment = ""; 857 mergeCommitComment = "";
812 858
813 foreach (Changeset *cs, currentHeads) { 859 foreach (Changeset *cs, currentHeads) {
1124 1170
1125 bool MainWindow::complainAboutCloneToExisting(QString arg) 1171 bool MainWindow::complainAboutCloneToExisting(QString arg)
1126 { 1172 {
1127 QMessageBox::critical 1173 QMessageBox::critical
1128 (this, tr("Path is in existing repository"), 1174 (this, tr("Path is in existing repository"),
1129 tr("<qt><b>Local path is in an existing repository</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is already inside an existing repository.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg))); 1175 tr("<qt><b>Local path is in an existing repository</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is already inside an existing repository.<br>Please provide a different folder name for the local repository.</qt>").arg(xmlEncode(arg)));
1130 return false; 1176 return false;
1131 } 1177 }
1132 1178
1133 bool MainWindow::complainAboutCloneToFile(QString arg) 1179 bool MainWindow::complainAboutCloneToFile(QString arg)
1134 { 1180 {
1136 (this, tr("Path is a file"), 1182 (this, tr("Path is a file"),
1137 tr("<qt><b>Local path is a file</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is an existing file.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg))); 1183 tr("<qt><b>Local path is a file</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This path is an existing file.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg)));
1138 return false; 1184 return false;
1139 } 1185 }
1140 1186
1141 bool MainWindow::complainAboutCloneToExistingFolder(QString arg) 1187 QString MainWindow::complainAboutCloneToExistingFolder(QString arg, QString remote)
1142 { 1188 {
1189 // If the directory "arg" exists but "arg" plus the last path
1190 // component of "remote" does not, then offer the latter as an
1191 // alternative path
1192
1193 QString offer;
1194
1195 QDir d(arg);
1196 if (d.exists()) {
1197 if (QRegExp("^\\w+://").indexIn(remote) >= 0) {
1198 QString rpath = QUrl(remote).path();
1199 if (rpath != "") {
1200 rpath = QDir(rpath).dirName();
1201 if (rpath != "" && !d.exists(rpath)) {
1202 offer = d.filePath(rpath);
1203 }
1204 }
1205 }
1206 }
1207
1208 if (offer != "") {
1209 bool result = (QMessageBox::question
1210 (this, tr("Folder exists"),
1211 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to \"%1\", but this folder already exists and so cannot be cloned to.<br><br>Would you like to create the new folder \"%2\" instead?</qt>")
1212 .arg(xmlEncode(arg)).arg(xmlEncode(offer)),
1213 QMessageBox::Ok | QMessageBox::Cancel,
1214 QMessageBox::Cancel)
1215 == QMessageBox::Ok);
1216 if (result) return offer;
1217 else return "";
1218 }
1219
1143 QMessageBox::critical 1220 QMessageBox::critical
1144 (this, tr("Folder exists"), 1221 (this, tr("Folder exists"),
1145 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to the local path \"%1\".<br>This is the path of an existing folder.<br>Please provide a new folder name for the local repository.</qt>").arg(xmlEncode(arg))); 1222 tr("<qt><b>Local folder already exists</b><br><br>You asked to open a remote repository by cloning it to \"%1\", but this file or folder already exists and so cannot be cloned to.<br>Please provide a different folder name for the local repository.</qt>").arg(xmlEncode(arg)));
1146 return false; 1223 return "";
1147 } 1224 }
1148 1225
1149 bool MainWindow::askToOpenParentRepo(QString arg, QString parent) 1226 bool MainWindow::askToOpenParentRepo(QString arg, QString parent)
1150 { 1227 {
1151 return (QMessageBox::question 1228 return (QMessageBox::question
1264 if (status == FolderUnknown) { 1341 if (status == FolderUnknown) {
1265 return complainAboutUnknownFolder(local); 1342 return complainAboutUnknownFolder(local);
1266 } 1343 }
1267 1344
1268 if (status == FolderExists) { 1345 if (status == FolderExists) {
1269 //!!! we can do better than this surely? 1346 local = complainAboutCloneToExistingFolder(local, remote);
1270 return complainAboutCloneToExistingFolder(local); 1347 if (local == "") return false;
1271 } 1348 }
1272 1349
1273 workFolderPath = local; 1350 workFolderPath = local;
1274 remoteRepoPath = remote; 1351 remoteRepoPath = remote;
1275 hgCloneFromRemote(); 1352 hgCloneFromRemote();
1712 1789
1713 case ACT_CLONEFROMREMOTE: 1790 case ACT_CLONEFROMREMOTE:
1714 MultiChoiceDialog::addRecentArgument("local", workFolderPath); 1791 MultiChoiceDialog::addRecentArgument("local", workFolderPath);
1715 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath); 1792 MultiChoiceDialog::addRecentArgument("remote", remoteRepoPath);
1716 MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true); 1793 MultiChoiceDialog::addRecentArgument("remote", workFolderPath, true);
1717 QMessageBox::information(this, "Clone", output); 1794 QMessageBox::information(this, tr("Clone"), tr("<qt><h3>Clone successful</h3><pre>%1</pre>").arg(xmlEncode(output)));
1718 enableDisableActions(); 1795 enableDisableActions();
1719 shouldHgStat = true; 1796 shouldHgStat = true;
1720 break; 1797 break;
1721 1798
1722 case ACT_LOG: 1799 case ACT_LOG:
2065 DEBUG << " head ids "<<endl; 2142 DEBUG << " head ids "<<endl;
2066 foreach (Changeset *h, currentHeads) { 2143 foreach (Changeset *h, currentHeads) {
2067 DEBUG << "head id = " << h->id() << endl; 2144 DEBUG << "head id = " << h->id() << endl;
2068 } 2145 }
2069 } 2146 }
2147 justMerged = false;
2070 } else if (currentParents.size() == 0) { 2148 } else if (currentParents.size() == 0) {
2071 if (currentHeads.size() == 0) { 2149 if (currentHeads.size() == 0) {
2072 // No heads -> empty repo 2150 // No heads -> empty repo
2073 emptyRepo = true; 2151 emptyRepo = true;
2074 } else { 2152 } else {
2076 // just converted this repo but haven't updated in it yet. 2154 // just converted this repo but haven't updated in it yet.
2077 // Uncommon but confusing; probably merits a special case 2155 // Uncommon but confusing; probably merits a special case
2078 noWorkingCopy = true; 2156 noWorkingCopy = true;
2079 canUpdate = true; 2157 canUpdate = true;
2080 } 2158 }
2159 justMerged = false;
2081 } else { 2160 } else {
2082 haveMerge = true; 2161 haveMerge = true;
2083 justMerged = true; 2162 justMerged = true;
2084 } 2163 }
2085 2164