Mercurial > hg > easyhg
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 |