comparison mainwindow.cpp @ 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
comparison
equal deleted inserted replaced
162:910c2c5d1873 163:5c262ac73948
45 { 45 {
46 QString wndTitle; 46 QString wndTitle;
47 47
48 fsWatcher = 0; 48 fsWatcher = 0;
49 commitsSincePush = 0; 49 commitsSincePush = 0;
50 shouldHgStat = true;
50 51
51 createActions(); 52 createActions();
52 createMenus(); 53 createMenus();
53 createToolBars(); 54 createToolBars();
54 createStatusBar(); 55 createStatusBar();
83 if (firstStart) { 84 if (firstStart) {
84 startupDialog(); 85 startupDialog();
85 } 86 }
86 87
87 findDiffBinaryName(); 88 findDiffBinaryName();
89 findMergeBinaryName();
88 90
89 ColourSet *cs = ColourSet::instance(); 91 ColourSet *cs = ColourSet::instance();
90 cs->clearDefaultNames(); 92 cs->clearDefaultNames();
91 cs->addDefaultName(""); 93 cs->addDefaultName("");
92 cs->addDefaultName("default"); 94 cs->addDefaultName("default");
159 void MainWindow::hgStat() 161 void MainWindow::hgStat()
160 { 162 {
161 QStringList params; 163 QStringList params;
162 params << "stat" << "-ardum"; 164 params << "stat" << "-ardum";
163 165
166 lastStatOutput = "";
167
164 // annoyingly, hg stat actually modifies the working directory -- 168 // annoyingly, hg stat actually modifies the working directory --
165 // it creates files called hg-checklink and hg-checkexec to test 169 // it creates files called hg-checklink and hg-checkexec to test
166 // properties of the filesystem 170 // properties of the filesystem
167 if (fsWatcher) fsWatcher->blockSignals(true); 171 if (fsWatcher) fsWatcher->blockSignals(true);
168 172
235 if (!currentFile.isEmpty()) 239 if (!currentFile.isEmpty())
236 { 240 {
237 params << "annotate" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ") 241 params << "annotate" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
238 242
239 runner->requestAction(HgAction(ACT_ANNOTATE, workFolderPath, params)); 243 runner->requestAction(HgAction(ACT_ANNOTATE, workFolderPath, params));
240 }
241 }
242
243 void MainWindow::hgResolveMark()
244 {
245 QStringList params;
246 QString currentFile;//!!! = hgTabs -> getCurrentFileListLine();
247
248 if (!currentFile.isEmpty())
249 {
250 params << "resolve" << "--mark" << "--" << currentFile.mid(2); //Jump over status marker characters (e.g "M ")
251
252 runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
253 } 244 }
254 } 245 }
255 246
256 void MainWindow::hgResolveList() 247 void MainWindow::hgResolveList()
257 { 248 {
329 << "--user" << getUserInfo(); 320 << "--user" << getUserInfo();
330 } 321 }
331 322
332 runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params)); 323 runner->requestAction(HgAction(ACT_COMMIT, workFolderPath, params));
333 } 324 }
334
335 justMerged = false;
336 } 325 }
337 326
338 QString MainWindow::filterTag(QString tag) 327 QString MainWindow::filterTag(QString tag)
339 { 328 {
340 for(int i = 0; i < tag.size(); i++) 329 for(int i = 0; i < tag.size(); i++)
422 } 411 }
423 } 412 }
424 diffBinaryName = diff; 413 diffBinaryName = diff;
425 } 414 }
426 415
416 void MainWindow::findMergeBinaryName()
417 {
418 QSettings settings;
419 QString merge = settings.value("mergebinary", "").toString();
420 if (merge == "") {
421 QStringList bases;
422 bases << "fmdiff3" << "kdiff3" << "meld" << "diffuse";
423 bool found = false;
424 foreach (QString base, bases) {
425 merge = findExecutable(base);
426 if (merge != base) {
427 found = true;
428 break;
429 }
430 }
431 if (found) {
432 settings.setValue("mergebinary", merge);
433 } else {
434 merge = "";
435 }
436 }
437 mergeBinaryName = merge;
438 }
439
427 void MainWindow::hgFolderDiff() 440 void MainWindow::hgFolderDiff()
428 { 441 {
429 if (diffBinaryName == "") return; 442 if (diffBinaryName == "") return;
430 443
431 QStringList params; 444 QStringList params;
441 } 454 }
442 455
443 456
444 void MainWindow::hgDiffToCurrent(QString id) 457 void MainWindow::hgDiffToCurrent(QString id)
445 { 458 {
459 if (diffBinaryName == "") return;
460
446 QStringList params; 461 QStringList params;
447 462
448 // Diff given revision against working folder 463 // Diff given revision against working folder
449 464
450 params << "--config" << "extensions.extdiff=" << "extdiff"; 465 params << "--config" << "extensions.extdiff=" << "extdiff";
455 } 470 }
456 471
457 472
458 void MainWindow::hgDiffToParent(QString child, QString parent) 473 void MainWindow::hgDiffToParent(QString child, QString parent)
459 { 474 {
475 if (diffBinaryName == "") return;
476
460 QStringList params; 477 QStringList params;
461 478
462 // Diff given revision against working folder 479 // Diff given revision against working folder
463 480
464 params << "--config" << "extensions.extdiff=" << "extdiff"; 481 params << "--config" << "extensions.extdiff=" << "extdiff";
506 tr("<h3>%1</h3><p>%2").arg(rf) 523 tr("<h3>%1</h3><p>%2").arg(rf)
507 .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:")), 524 .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:")),
508 tr("<h3>%1</h3><p>%2").arg(rf) 525 tr("<h3>%1</h3><p>%2").arg(rf)
509 .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())), 526 .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())),
510 files)) { 527 files)) {
528
529 lastRevertedFiles = files;
511 530
512 if (files.empty()) { 531 if (files.empty()) {
513 params << "revert" << "--no-backup"; 532 params << "revert" << "--no-backup";
514 } else { 533 } else {
515 params << "revert" << "--no-backup" << "--" << files; 534 params << "revert" << "--no-backup" << "--" << files;
516 } 535 }
536
537 //!!! This is problematic. If you've got an uncommitted
538 //!!! merge, you can't revert it without declaring which
539 //!!! parent of the merge you want to revert to (reasonably
540 //!!! enough). We're OK if we just did the merge in easyhg a
541 //!!! moment ago, because we have a record of which parent was
542 //!!! the target -- but if you exit and restart, we've lost
543 //!!! that record and it doesn't appear to be possible to get
544 //!!! it back from Hg. Even if you just switched from one
545 //!!! repo to another, the record is lost. What to do?
546
547 if (justMerged && mergeTargetRevision != "") {
548 params << "--rev" << mergeTargetRevision;
549 }
517 550
518 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params)); 551 runner->requestAction(HgAction(ACT_REVERT, workFolderPath, params));
519 } 552 }
520 } 553 }
521 554
555
556 void MainWindow::hgMarkResolved(QStringList files)
557 {
558 QStringList params;
559
560 params << "resolve" << "--mark";
561
562 if (files.empty()) {
563 params << "--all";
564 } else {
565 params << files;
566 }
567
568 runner->requestAction(HgAction(ACT_RESOLVE_MARK, workFolderPath, params));
569 }
570
571
522 void MainWindow::hgRetryMerge() 572 void MainWindow::hgRetryMerge()
523 { 573 {
524 QStringList params; 574 QStringList params;
525 575
526 params << "resolve" << "--all"; 576 params << "resolve";
577
578 if (mergeBinaryName != "") {
579 params << "--tool" << mergeBinaryName;
580 }
581
582 QStringList files = hgTabs->getSelectedUnresolvedFiles();
583 if (files.empty()) {
584 params << "--all";
585 } else {
586 params << files;
587 }
588
527 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params)); 589 runner->requestAction(HgAction(ACT_RETRY_MERGE, workFolderPath, params));
590
591 mergeCommitComment = tr("Merge");
528 } 592 }
529 593
530 594
531 void MainWindow::hgMerge() 595 void MainWindow::hgMerge()
532 { 596 {
597 if (hgTabs->canResolve()) {
598 hgRetryMerge();
599 return;
600 }
601
533 QStringList params; 602 QStringList params;
534 603
535 params << "merge"; 604 params << "merge";
536 605
606 if (mergeBinaryName != "") {
607 params << "--tool" << mergeBinaryName;
608 }
609
610 if (currentParents.size() == 1) {
611 mergeTargetRevision = currentParents[0]->id();
612 }
613
537 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); 614 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
538 615
539 mergeCommitComment = tr("Merge"); 616 mergeCommitComment = tr("Merge");
540 } 617 }
541 618
544 { 621 {
545 QStringList params; 622 QStringList params;
546 623
547 params << "merge"; 624 params << "merge";
548 params << "--rev" << Changeset::hashOf(id); 625 params << "--rev" << Changeset::hashOf(id);
626
627 if (mergeBinaryName != "") {
628 params << "--tool" << mergeBinaryName;
629 }
549 630
550 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params)); 631 runner->requestAction(HgAction(ACT_MERGE, workFolderPath, params));
551 632
552 mergeCommitComment = ""; 633 mergeCommitComment = "";
553 634
671 foreach (Changeset *cs, currentParents) delete cs; 752 foreach (Changeset *cs, currentParents) delete cs;
672 currentParents.clear(); 753 currentParents.clear();
673 foreach (Changeset *cs, currentHeads) delete cs; 754 foreach (Changeset *cs, currentHeads) delete cs;
674 currentHeads.clear(); 755 currentHeads.clear();
675 currentBranch = ""; 756 currentBranch = "";
757 lastStatOutput = "";
758 lastRevertedFiles.clear();
759 mergeTargetRevision = "";
760 mergeCommitComment = "";
676 needNewLog = true; 761 needNewLog = true;
677 } 762 }
678 763
679 void MainWindow::hgServe() 764 void MainWindow::hgServe()
680 { 765 {
1196 .arg(xmlEncode(output.left(800)) 1281 .arg(xmlEncode(output.left(800))
1197 .replace("\n", "<br>")) 1282 .replace("\n", "<br>"))
1198 : ""); 1283 : "");
1199 1284
1200 QMessageBox::warning(this, tr("Command failed"), message); 1285 QMessageBox::warning(this, tr("Command failed"), message);
1201
1202 /* todo:
1203 if ((runningAction == ACT_MERGE) && (exitCode != 0))
1204 {
1205 // If we had a failed merge, offer to retry
1206 if (QMessageBox::Ok == QMessageBox::information(this, tr("Retry merge ?"), tr("Merge attempt failed. retry ?"), QMessageBox::Ok | QMessageBox::Cancel))
1207 {
1208 runningAction = ACT_NONE;
1209 hgRetryMerge();
1210 }
1211 else
1212 {
1213 runningAction = ACT_NONE;
1214 hgStat();
1215 }
1216 }
1217 else
1218 {
1219 */
1220 } 1286 }
1221 1287
1222 void MainWindow::commandCompleted(HgAction completedAction, QString output) 1288 void MainWindow::commandCompleted(HgAction completedAction, QString output)
1223 { 1289 {
1224 HGACTIONS action = completedAction.action; 1290 HGACTIONS action = completedAction.action;
1225 1291
1226 if (action == ACT_NONE) return; 1292 if (action == ACT_NONE) return;
1227 1293
1228 bool shouldHgStat = false;
1229 bool headsChanged = false; 1294 bool headsChanged = false;
1230 QStringList oldHeadIds; 1295 QStringList oldHeadIds;
1231 1296
1232 switch (action) { 1297 switch (action) {
1233 1298
1251 currentBranch = output.trimmed(); 1316 currentBranch = output.trimmed();
1252 break; 1317 break;
1253 1318
1254 case ACT_STAT: 1319 case ACT_STAT:
1255 if (fsWatcher) fsWatcher->blockSignals(false); 1320 if (fsWatcher) fsWatcher->blockSignals(false);
1256 hgTabs->updateWorkFolderFileList(output); 1321 lastStatOutput = output;
1257 updateFileSystemWatcher(); 1322 updateFileSystemWatcher();
1323 break;
1324
1325 case ACT_RESOLVE_LIST:
1326 if (output != "") {
1327 // Remove lines beginning with R (they are resolved,
1328 // and the file stat parser treats R as removed)
1329 QStringList outList = output.split('\n');
1330 QStringList winnowed;
1331 foreach (QString line, outList) {
1332 if (!line.startsWith("R ")) winnowed.push_back(line);
1333 }
1334 output = winnowed.join("\n");
1335 }
1336 DEBUG << "lastStatOutput = " << lastStatOutput << endl;
1337 DEBUG << "output = " << output << endl;
1338 hgTabs->updateWorkFolderFileList(lastStatOutput + output);
1339 break;
1340
1341 case ACT_RESOLVE_MARK:
1342 shouldHgStat = true;
1258 break; 1343 break;
1259 1344
1260 case ACT_INCOMING: 1345 case ACT_INCOMING:
1261 showIncoming(output); 1346 showIncoming(output);
1262 break; 1347 break;
1263 1348
1264 case ACT_ANNOTATE: 1349 case ACT_ANNOTATE:
1265 case ACT_RESOLVE_LIST:
1266 case ACT_RESOLVE_MARK:
1267 presentLongStdoutToUser(output); 1350 presentLongStdoutToUser(output);
1268 shouldHgStat = true; 1351 shouldHgStat = true;
1269 break; 1352 break;
1270 1353
1271 case ACT_PULL: 1354 case ACT_PULL:
1325 } 1408 }
1326 break; 1409 break;
1327 1410
1328 case ACT_COMMIT: 1411 case ACT_COMMIT:
1329 hgTabs->clearSelections(); 1412 hgTabs->clearSelections();
1413 justMerged = false;
1330 shouldHgStat = true; 1414 shouldHgStat = true;
1415 break;
1416
1417 case ACT_REVERT:
1418 hgMarkResolved(lastRevertedFiles);
1419 justMerged = false;
1331 break; 1420 break;
1332 1421
1333 case ACT_REMOVE: 1422 case ACT_REMOVE:
1334 case ACT_ADD: 1423 case ACT_ADD:
1335 case ACT_REVERT:
1336 hgTabs->clearSelections(); 1424 hgTabs->clearSelections();
1337 shouldHgStat = true; 1425 shouldHgStat = true;
1338 break; 1426 break;
1339 1427
1340 case ACT_FOLDERDIFF: 1428 case ACT_FOLDERDIFF:
1355 shouldHgStat = true; 1443 shouldHgStat = true;
1356 justMerged = true; 1444 justMerged = true;
1357 break; 1445 break;
1358 1446
1359 case ACT_RETRY_MERGE: 1447 case ACT_RETRY_MERGE:
1360 QMessageBox::information(this, tr("Merge retry"), 1448 QMessageBox::information(this, tr("Resolved"),
1361 tr("Merge retry successful.")); 1449 tr("<qt><h3>Merge resolved</h3><p>Merge resolved successfully.</p>"));
1362 shouldHgStat = true; 1450 shouldHgStat = true;
1363 justMerged = true; 1451 justMerged = true;
1364 break; 1452 break;
1365 1453
1366 default: 1454 default:
1367 break; 1455 break;
1368 } 1456 }
1369 1457
1370 // Sequence when no full log required: 1458 // Sequence when no full log required:
1371 // paths -> branch -> stat -> heads -> 1459 // paths -> branch -> stat -> resolve-list -> heads ->
1372 // incremental-log (only if heads changed) -> parents 1460 // incremental-log (only if heads changed) -> parents
1373 // 1461 //
1374 // Sequence when full log required: 1462 // Sequence when full log required:
1375 // paths -> branch -> stat -> heads -> parents -> log 1463 // paths -> branch -> stat -> resolve-list -> heads -> parents -> log
1376 // 1464 //
1377 // Note we want to call enableDisableActions only once, at the end 1465 // Note we want to call enableDisableActions only once, at the end
1378 // of whichever sequence is in use. 1466 // of whichever sequence is in use.
1379 1467
1380 bool noMore = false; 1468 bool noMore = false;
1388 case ACT_QUERY_BRANCH: 1476 case ACT_QUERY_BRANCH:
1389 hgStat(); 1477 hgStat();
1390 break; 1478 break;
1391 1479
1392 case ACT_STAT: 1480 case ACT_STAT:
1481 hgResolveList();
1482 break;
1483
1484 case ACT_RESOLVE_LIST:
1393 hgQueryHeads(); 1485 hgQueryHeads();
1394 break; 1486 break;
1395 1487
1396 case ACT_QUERY_HEADS: 1488 case ACT_QUERY_HEADS:
1397 if (headsChanged && !needNewLog) { 1489 if (headsChanged && !needNewLog) {
1418 // we're done 1510 // we're done
1419 noMore = true; 1511 noMore = true;
1420 1512
1421 default: 1513 default:
1422 if (shouldHgStat) { 1514 if (shouldHgStat) {
1515 shouldHgStat = false;
1423 hgQueryPaths(); 1516 hgQueryPaths();
1424 } else { 1517 } else {
1425 noMore = true; 1518 noMore = true;
1426 } 1519 }
1427 break; 1520 break;
1446 connect(hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff())); 1539 connect(hgFolderDiffAct, SIGNAL(triggered()), this, SLOT(hgFolderDiff()));
1447 // connect(hgChgSetDiffAct, SIGNAL(triggered()), this, SLOT(hgChgSetDiff())); 1540 // connect(hgChgSetDiffAct, SIGNAL(triggered()), this, SLOT(hgChgSetDiff()));
1448 connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate())); 1541 connect(hgUpdateAct, SIGNAL(triggered()), this, SLOT(hgUpdate()));
1449 connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert())); 1542 connect(hgRevertAct, SIGNAL(triggered()), this, SLOT(hgRevert()));
1450 connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge())); 1543 connect(hgMergeAct, SIGNAL(triggered()), this, SLOT(hgMerge()));
1451 connect(hgRetryMergeAct, SIGNAL(triggered()), this, SLOT(hgRetryMerge()));
1452 connect(hgTagAct, SIGNAL(triggered()), this, SLOT(hgTag())); 1544 connect(hgTagAct, SIGNAL(triggered()), this, SLOT(hgTag()));
1453 connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore())); 1545 connect(hgIgnoreAct, SIGNAL(triggered()), this, SLOT(hgIgnore()));
1454 1546
1455 connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings())); 1547 connect(settingsAct, SIGNAL(triggered()), this, SLOT(settings()));
1456 connect(openAct, SIGNAL(triggered()), this, SLOT(open())); 1548 connect(openAct, SIGNAL(triggered()), this, SLOT(open()));
1463 1555
1464 // connect(hgTabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int))); 1556 // connect(hgTabs, SIGNAL(currentChanged(int)), this, SLOT(tabChanged(int)));
1465 1557
1466 // connect(hgUpdateToRevAct, SIGNAL(triggered()), this, SLOT(hgUpdateToRev())); 1558 // connect(hgUpdateToRevAct, SIGNAL(triggered()), this, SLOT(hgUpdateToRev()));
1467 connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate())); 1559 connect(hgAnnotateAct, SIGNAL(triggered()), this, SLOT(hgAnnotate()));
1468 connect(hgResolveListAct, SIGNAL(triggered()), this, SLOT(hgResolveList()));
1469 connect(hgResolveMarkAct, SIGNAL(triggered()), this, SLOT(hgResolveMark()));
1470 connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe())); 1560 connect(hgServeAct, SIGNAL(triggered()), this, SLOT(hgServe()));
1471 connect(clearSelectionsAct, SIGNAL(triggered()), this, SLOT(clearSelections())); 1561 connect(clearSelectionsAct, SIGNAL(triggered()), this, SLOT(clearSelections()));
1472 } 1562 }
1473 1563
1474 void MainWindow::connectTabsSignals() 1564 void MainWindow::connectTabsSignals()
1560 hgAddAct -> setEnabled(localRepoActionsEnabled); 1650 hgAddAct -> setEnabled(localRepoActionsEnabled);
1561 hgRemoveAct -> setEnabled(localRepoActionsEnabled); 1651 hgRemoveAct -> setEnabled(localRepoActionsEnabled);
1562 hgUpdateAct -> setEnabled(localRepoActionsEnabled); 1652 hgUpdateAct -> setEnabled(localRepoActionsEnabled);
1563 hgCommitAct -> setEnabled(localRepoActionsEnabled); 1653 hgCommitAct -> setEnabled(localRepoActionsEnabled);
1564 hgMergeAct -> setEnabled(localRepoActionsEnabled); 1654 hgMergeAct -> setEnabled(localRepoActionsEnabled);
1565 hgRetryMergeAct -> setEnabled(localRepoActionsEnabled);
1566 hgResolveListAct -> setEnabled(localRepoActionsEnabled);
1567 hgResolveMarkAct -> setEnabled(localRepoActionsEnabled);
1568 hgAnnotateAct -> setEnabled(localRepoActionsEnabled); 1655 hgAnnotateAct -> setEnabled(localRepoActionsEnabled);
1569 hgServeAct -> setEnabled(localRepoActionsEnabled); 1656 hgServeAct -> setEnabled(localRepoActionsEnabled);
1570 hgTagAct -> setEnabled(localRepoActionsEnabled); 1657 hgTagAct -> setEnabled(localRepoActionsEnabled);
1571 hgIgnoreAct -> setEnabled(localRepoActionsEnabled); 1658 hgIgnoreAct -> setEnabled(localRepoActionsEnabled);
1572 1659
1577 1664
1578 //!!! new stuff: 1665 //!!! new stuff:
1579 hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd()); 1666 hgAddAct->setEnabled(localRepoActionsEnabled && hgTabs->canAdd());
1580 hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove()); 1667 hgRemoveAct->setEnabled(localRepoActionsEnabled && hgTabs->canRemove());
1581 hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); 1668 hgCommitAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit());
1582 hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canCommit()); 1669 hgRevertAct->setEnabled(localRepoActionsEnabled && hgTabs->canRevert());
1583 hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDoDiff()); 1670 hgFolderDiffAct->setEnabled(localRepoActionsEnabled && hgTabs->canDiff());
1584 1671
1585 // A default merge makes sense if: 1672 // A default merge makes sense if:
1586 // * there is only one parent (if there are two, we have an uncommitted merge) and 1673 // * there is only one parent (if there are two, we have an uncommitted merge) and
1587 // * there are exactly two heads that have the same branch as the current branch and 1674 // * there are exactly two heads that have the same branch as the current branch and
1588 // * our parent is one of those heads 1675 // * our parent is one of those heads
1622 } 1709 }
1623 } else if (currentParents.size() == 0) { 1710 } else if (currentParents.size() == 0) {
1624 emptyRepo = true; 1711 emptyRepo = true;
1625 } else { 1712 } else {
1626 haveMerge = true; 1713 haveMerge = true;
1627 } 1714 justMerged = true;
1628 1715 }
1629 hgMergeAct->setEnabled(localRepoActionsEnabled && canMerge); 1716
1630 hgUpdateAct->setEnabled(localRepoActionsEnabled && canUpdate); 1717 hgMergeAct->setEnabled(localRepoActionsEnabled &&
1718 (canMerge || hgTabs->canResolve()));
1719 hgUpdateAct->setEnabled(localRepoActionsEnabled &&
1720 (canUpdate && !hgTabs->canRevert()));
1631 1721
1632 // Set the state field on the file status widget 1722 // Set the state field on the file status widget
1633 1723
1634 QString branchText; 1724 QString branchText;
1635 if (currentBranch == "" || currentBranch == "default") { 1725 if (currentBranch == "" || currentBranch == "default") {
1642 1732
1643 if (emptyRepo) { 1733 if (emptyRepo) {
1644 hgTabs->setState(tr("Nothing committed to this repository yet")); 1734 hgTabs->setState(tr("Nothing committed to this repository yet"));
1645 } else if (canMerge) { 1735 } else if (canMerge) {
1646 hgTabs->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText)); 1736 hgTabs->setState(tr("<b>Awaiting merge</b> on %1").arg(branchText));
1737 } else if (!hgTabs->getAllUnresolvedFiles().empty()) {
1738 hgTabs->setState(tr("Have unresolved files following merge on %1").arg(branchText));
1647 } else if (haveMerge) { 1739 } else if (haveMerge) {
1648 hgTabs->setState(tr("Have merged but not yet committed on %1").arg(branchText)); 1740 hgTabs->setState(tr("Have merged but not yet committed on %1").arg(branchText));
1649 } else if (canUpdate) { 1741 } else if (canUpdate) {
1650 hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText)); 1742 if (hgTabs->canRevert()) {
1743 // have uncommitted changes
1744 hgTabs->setState(tr("On %1. Not at the head of the branch").arg(branchText));
1745 } else {
1746 // no uncommitted changes
1747 hgTabs->setState(tr("On %1. Not at the head of the branch: consider updating").arg(branchText));
1748 }
1651 } else if (currentBranchHeads > 1) { 1749 } else if (currentBranchHeads > 1) {
1652 hgTabs->setState(tr("At one of %n heads of %1", "", currentBranchHeads).arg(branchText)); 1750 hgTabs->setState(tr("At one of %n heads of %1", "", currentBranchHeads).arg(branchText));
1653 } else { 1751 } else {
1654 hgTabs->setState(tr("At the head of %1").arg(branchText)); 1752 hgTabs->setState(tr("At the head of %1").arg(branchText));
1655 } 1753 }
1719 /* hgUpdateToRevAct = new QAction(tr("Update to revision"), this); 1817 /* hgUpdateToRevAct = new QAction(tr("Update to revision"), this);
1720 hgUpdateToRevAct -> setStatusTip(tr("Update working folder to version selected from history list")); 1818 hgUpdateToRevAct -> setStatusTip(tr("Update working folder to version selected from history list"));
1721 */ 1819 */
1722 hgAnnotateAct = new QAction(tr("Annotate"), this); 1820 hgAnnotateAct = new QAction(tr("Annotate"), this);
1723 hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file")); 1821 hgAnnotateAct -> setStatusTip(tr("Show line-by-line version information for selected file"));
1724
1725 hgResolveListAct = new QAction(tr("Resolve (list)"), this);
1726 hgResolveListAct -> setStatusTip(tr("Resolve (list): Show list of files needing merge"));
1727
1728 hgResolveMarkAct = new QAction(tr("Resolve (mark)"), this);
1729 hgResolveMarkAct -> setStatusTip(tr("Resolve (mark): Mark selected file status as resolved"));
1730
1731 hgRetryMergeAct = new QAction(tr("Retry merge"), this);
1732 hgRetryMergeAct -> setStatusTip(tr("Retry merge after failed merge attempt."));
1733 1822
1734 hgTagAct = new QAction(tr("Tag revision"), this); 1823 hgTagAct = new QAction(tr("Tag revision"), this);
1735 hgTagAct -> setStatusTip(tr("Give decsriptive name (tag) to current workfolder parent revision.")); 1824 hgTagAct -> setStatusTip(tr("Give decsriptive name (tag) to current workfolder parent revision."));
1736 1825
1737 hgIgnoreAct = new QAction(tr("Edit .hgignore"), this); 1826 hgIgnoreAct = new QAction(tr("Edit .hgignore"), this);