834 class MergeFailure(util.Abort): |
834 class MergeFailure(util.Abort): |
835 pass |
835 pass |
836 |
836 |
837 def relocate(repo, orig, dest): |
837 def relocate(repo, orig, dest): |
838 """rewrite <rev> on dest""" |
838 """rewrite <rev> on dest""" |
|
839 if orig.rev() == dest.rev(): |
|
840 raise util.Abort(_('tried to relocate a node on top of itself'), |
|
841 hint=_("This shouldn't happen. If you still " |
|
842 "need to move changesets, please do so " |
|
843 "manually with nothing to rebase - working " |
|
844 "directory parent is also destination")) |
|
845 |
|
846 rebase = extensions.find('rebase') |
|
847 # dummy state to trick rebase node |
|
848 if not orig.p2().rev() == node.nullrev: |
|
849 raise util.Abort( |
|
850 'no support for evolving merge changesets yet', |
|
851 hint="Redo the merge a use `hg prune` to obsolete the old one") |
|
852 destbookmarks = repo.nodebookmarks(dest.node()) |
|
853 nodesrc = orig.node() |
|
854 destphase = repo[nodesrc].phase() |
839 try: |
855 try: |
840 if orig.rev() == dest.rev(): |
856 r = rebase.rebasenode(repo, orig.node(), dest.node(), |
841 raise util.Abort(_('tried to relocate a node on top of itself'), |
857 {node.nullrev: node.nullrev}, False) |
842 hint=_("This shouldn't happen. If you still " |
858 if r[-1]: #some conflict |
843 "need to move changesets, please do so " |
|
844 "manually with nothing to rebase - working " |
|
845 "directory parent is also destination")) |
|
846 |
|
847 rebase = extensions.find('rebase') |
|
848 # dummy state to trick rebase node |
|
849 if not orig.p2().rev() == node.nullrev: |
|
850 raise util.Abort( |
859 raise util.Abort( |
851 'no support for evolving merge changesets yet', |
860 'unresolved merge conflicts (see hg help resolve)') |
852 hint="Redo the merge a use `hg prune` to obsolete the old one") |
861 cmdutil.duplicatecopies(repo, orig.node(), dest.node()) |
853 destbookmarks = repo.nodebookmarks(dest.node()) |
862 nodenew = rebase.concludenode(repo, orig.node(), dest.node(), |
854 nodesrc = orig.node() |
863 node.nullid) |
855 destphase = repo[nodesrc].phase() |
864 except util.Abort, exc: |
856 wlock = lock = None |
865 class LocalMergeFailure(MergeFailure, exc.__class__): |
857 try: |
866 pass |
858 wlock = repo.wlock() |
867 exc.__class__ = LocalMergeFailure |
859 lock = repo.lock() |
868 raise |
860 r = rebase.rebasenode(repo, orig.node(), dest.node(), |
869 oldbookmarks = repo.nodebookmarks(nodesrc) |
861 {node.nullrev: node.nullrev}, False) |
870 if nodenew is not None: |
862 if r[-1]: #some conflict |
871 retractboundary(repo, destphase, [nodenew]) |
863 raise util.Abort( |
872 createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))]) |
864 'unresolved merge conflicts (see hg help resolve)') |
873 for book in oldbookmarks: |
865 cmdutil.duplicatecopies(repo, orig.node(), dest.node()) |
874 repo._bookmarks[book] = nodenew |
866 nodenew = rebase.concludenode(repo, orig.node(), dest.node(), |
875 else: |
867 node.nullid) |
876 createmarkers(repo, [(repo[nodesrc], ())]) |
868 except util.Abort, exc: |
877 # Behave like rebase, move bookmarks to dest |
869 class LocalMergeFailure(MergeFailure, exc.__class__): |
878 for book in oldbookmarks: |
870 pass |
|
871 exc.__class__ = LocalMergeFailure |
|
872 raise |
|
873 finally: |
|
874 lockmod.release(lock, wlock) |
|
875 oldbookmarks = repo.nodebookmarks(nodesrc) |
|
876 if nodenew is not None: |
|
877 retractboundary(repo, destphase, [nodenew]) |
|
878 createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))]) |
|
879 for book in oldbookmarks: |
|
880 repo._bookmarks[book] = nodenew |
|
881 else: |
|
882 createmarkers(repo, [(repo[nodesrc], ())]) |
|
883 # Behave like rebase, move bookmarks to dest |
|
884 for book in oldbookmarks: |
|
885 repo._bookmarks[book] = dest.node() |
|
886 for book in destbookmarks: # restore bookmark that rebase move |
|
887 repo._bookmarks[book] = dest.node() |
879 repo._bookmarks[book] = dest.node() |
888 if oldbookmarks or destbookmarks: |
880 for book in destbookmarks: # restore bookmark that rebase move |
889 repo._bookmarks.write() |
881 repo._bookmarks[book] = dest.node() |
890 return nodenew |
882 if oldbookmarks or destbookmarks: |
891 except util.Abort: |
883 repo._bookmarks.write() |
892 # Invalidate the previous setparents |
884 return nodenew |
893 repo.dirstate.invalidate() |
|
894 raise |
|
895 |
885 |
896 def _bookmarksupdater(repo, oldid): |
886 def _bookmarksupdater(repo, oldid): |
897 """Return a callable update(newid) updating the current bookmark |
887 """Return a callable update(newid) updating the current bookmark |
898 and bookmarks bound to oldid to newid. |
888 and bookmarks bound to oldid to newid. |
899 """ |
889 """ |
1401 repo.ui.write('hg update %s;\n' % prec) |
1397 repo.ui.write('hg update %s;\n' % prec) |
1402 repo.ui.write('hg revert --all --rev %s;\n' % bumped) |
1398 repo.ui.write('hg revert --all --rev %s;\n' % bumped) |
1403 repo.ui.write('hg commit --msg "bumped update to %s"') |
1399 repo.ui.write('hg commit --msg "bumped update to %s"') |
1404 return 0 |
1400 return 0 |
1405 if progresscb: progresscb() |
1401 if progresscb: progresscb() |
1406 wlock = repo.wlock() |
1402 newid = tmpctx = None |
|
1403 tmpctx = bumped |
|
1404 bmupdate = _bookmarksupdater(repo, bumped.node()) |
|
1405 # Basic check for common parent. Far too complicated and fragile |
|
1406 tr = repo.transaction('bumped-stabilize') |
1407 try: |
1407 try: |
1408 newid = tmpctx = None |
1408 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1409 tmpctx = bumped |
1409 # Need to rebase the changeset at the right place |
1410 lock = repo.lock() |
1410 repo.ui.status( |
1411 try: |
1411 _('rebasing to destination parent: %s\n') % prec.p1()) |
1412 bmupdate = _bookmarksupdater(repo, bumped.node()) |
|
1413 # Basic check for common parent. Far too complicated and fragile |
|
1414 tr = repo.transaction('bumped-stabilize') |
|
1415 try: |
1412 try: |
1416 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1413 tmpid = relocate(repo, bumped, prec.p1()) |
1417 # Need to rebase the changeset at the right place |
1414 if tmpid is not None: |
1418 repo.ui.status( |
1415 tmpctx = repo[tmpid] |
1419 _('rebasing to destination parent: %s\n') % prec.p1()) |
1416 createmarkers(repo, [(bumped, (tmpctx,))]) |
1420 try: |
1417 except MergeFailure: |
1421 tmpid = relocate(repo, bumped, prec.p1()) |
1418 repo.opener.write('graftstate', bumped.hex() + '\n') |
1422 if tmpid is not None: |
1419 repo.ui.write_err(_('evolution failed!\n')) |
1423 tmpctx = repo[tmpid] |
1420 repo.ui.write_err( |
1424 createmarkers(repo, [(bumped, (tmpctx,))]) |
1421 _('fix conflict and run "hg evolve --continue"\n')) |
1425 except MergeFailure: |
1422 raise |
1426 repo.opener.write('graftstate', bumped.hex() + '\n') |
1423 # Create the new commit context |
1427 repo.ui.write_err(_('evolution failed!\n')) |
1424 repo.ui.status(_('computing new diff\n')) |
1428 repo.ui.write_err( |
1425 files = set() |
1429 _('fix conflict and run "hg evolve --continue"\n')) |
1426 copied = copies.pathcopies(prec, bumped) |
1430 raise |
1427 precmanifest = prec.manifest() |
1431 # Create the new commit context |
1428 for key, val in bumped.manifest().iteritems(): |
1432 repo.ui.status(_('computing new diff\n')) |
1429 if precmanifest.pop(key, None) != val: |
1433 files = set() |
1430 files.add(key) |
1434 copied = copies.pathcopies(prec, bumped) |
1431 files.update(precmanifest) # add missing files |
1435 precmanifest = prec.manifest() |
1432 # commit it |
1436 for key, val in bumped.manifest().iteritems(): |
1433 if files: # something to commit! |
1437 if precmanifest.pop(key, None) != val: |
1434 def filectxfn(repo, ctx, path): |
1438 files.add(key) |
1435 if path in bumped: |
1439 files.update(precmanifest) # add missing files |
1436 fctx = bumped[path] |
1440 # commit it |
1437 flags = fctx.flags() |
1441 if files: # something to commit! |
1438 mctx = memfilectx(repo, fctx.path(), fctx.data(), |
1442 def filectxfn(repo, ctx, path): |
1439 islink='l' in flags, |
1443 if path in bumped: |
1440 isexec='x' in flags, |
1444 fctx = bumped[path] |
1441 copied=copied.get(path)) |
1445 flags = fctx.flags() |
1442 return mctx |
1446 mctx = memfilectx(repo, fctx.path(), fctx.data(), |
1443 raise IOError() |
1447 islink='l' in flags, |
1444 text = 'bumped update to %s:\n\n' % prec |
1448 isexec='x' in flags, |
1445 text += bumped.description() |
1449 copied=copied.get(path)) |
1446 |
1450 return mctx |
1447 new = context.memctx(repo, |
1451 raise IOError() |
1448 parents=[prec.node(), node.nullid], |
1452 text = 'bumped update to %s:\n\n' % prec |
1449 text=text, |
1453 text += bumped.description() |
1450 files=files, |
1454 |
1451 filectxfn=filectxfn, |
1455 new = context.memctx(repo, |
1452 user=bumped.user(), |
1456 parents=[prec.node(), node.nullid], |
1453 date=bumped.date(), |
1457 text=text, |
1454 extra=bumped.extra()) |
1458 files=files, |
1455 |
1459 filectxfn=filectxfn, |
1456 newid = repo.commitctx(new) |
1460 user=bumped.user(), |
1457 if newid is None: |
1461 date=bumped.date(), |
1458 createmarkers(repo, [(tmpctx, ())]) |
1462 extra=bumped.extra()) |
1459 newid = prec.node() |
1463 |
1460 else: |
1464 newid = repo.commitctx(new) |
1461 retractboundary(repo, bumped.phase(), [newid]) |
1465 if newid is None: |
1462 createmarkers(repo, [(tmpctx, (repo[newid],))], |
1466 createmarkers(repo, [(tmpctx, ())]) |
1463 flag=obsolete.bumpedfix) |
1467 newid = prec.node() |
1464 bmupdate(newid) |
1468 else: |
1465 tr.close() |
1469 retractboundary(repo, bumped.phase(), [newid]) |
1466 repo.ui.status(_('committed as %s\n') % node.short(newid)) |
1470 createmarkers(repo, [(tmpctx, (repo[newid],))], |
|
1471 flag=obsolete.bumpedfix) |
|
1472 bmupdate(newid) |
|
1473 tr.close() |
|
1474 repo.ui.status(_('committed as %s\n') % node.short(newid)) |
|
1475 finally: |
|
1476 tr.release() |
|
1477 finally: |
|
1478 lock.release() |
|
1479 # reroute the working copy parent to the new changeset |
|
1480 repo.dirstate.setparents(newid, node.nullid) |
|
1481 finally: |
1467 finally: |
1482 wlock.release() |
1468 tr.release() |
|
1469 # reroute the working copy parent to the new changeset |
|
1470 repo.dirstate.setparents(newid, node.nullid) |
1483 |
1471 |
1484 def _solvedivergent(ui, repo, divergent, dryrun=False, progresscb=None): |
1472 def _solvedivergent(ui, repo, divergent, dryrun=False, progresscb=None): |
1485 base, others = divergentdata(divergent) |
1473 base, others = divergentdata(divergent) |
1486 if len(others) > 1: |
1474 if len(others) > 1: |
1487 othersstr = "[%s]" % (','.join([str(i) for i in others])) |
1475 othersstr = "[%s]" % (','.join([str(i) for i in others])) |
1535 ui.write('hg up -C %s &&\n' % base) |
1523 ui.write('hg up -C %s &&\n' % base) |
1536 ui.write('hg revert --all --rev tip &&\n') |
1524 ui.write('hg revert --all --rev tip &&\n') |
1537 ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n' |
1525 ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n' |
1538 % divergent) |
1526 % divergent) |
1539 return |
1527 return |
1540 wlock = lock = None |
1528 if divergent not in repo[None].parents(): |
1541 try: |
1529 repo.ui.status(_('updating to "local" conflict\n')) |
1542 wlock = repo.wlock() |
1530 hg.update(repo, divergent.rev()) |
1543 lock = repo.lock() |
1531 repo.ui.note(_('merging divergent changeset\n')) |
1544 if divergent not in repo[None].parents(): |
1532 if progresscb: progresscb() |
1545 repo.ui.status(_('updating to "local" conflict\n')) |
1533 stats = merge.update(repo, |
1546 hg.update(repo, divergent.rev()) |
1534 other.node(), |
1547 repo.ui.note(_('merging divergent changeset\n')) |
1535 branchmerge=True, |
1548 if progresscb: progresscb() |
1536 force=False, |
1549 stats = merge.update(repo, |
1537 partial=None, |
1550 other.node(), |
1538 ancestor=base.node(), |
1551 branchmerge=True, |
1539 mergeancestor=True) |
1552 force=False, |
1540 hg._showstats(repo, stats) |
1553 partial=None, |
1541 if stats[3]: |
1554 ancestor=base.node(), |
1542 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " |
1555 mergeancestor=True) |
1543 "or 'hg update -C .' to abandon\n")) |
1556 hg._showstats(repo, stats) |
1544 if stats[3] > 0: |
1557 if stats[3]: |
1545 raise util.Abort('merge conflict between several amendments ' |
1558 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " |
1546 '(this is not automated yet)', |
1559 "or 'hg update -C .' to abandon\n")) |
1547 hint="""/!\ You can try: |
1560 if stats[3] > 0: |
|
1561 raise util.Abort('merge conflict between several amendments ' |
|
1562 '(this is not automated yet)', |
|
1563 hint="""/!\ You can try: |
|
1564 /!\ * manual merge + resolve => new cset X |
1548 /!\ * manual merge + resolve => new cset X |
1565 /!\ * hg up to the parent of the amended changeset (which are named W and Z) |
1549 /!\ * hg up to the parent of the amended changeset (which are named W and Z) |
1566 /!\ * hg revert --all -r X |
1550 /!\ * hg revert --all -r X |
1567 /!\ * hg ci -m "same message as the amended changeset" => new cset Y |
1551 /!\ * hg ci -m "same message as the amended changeset" => new cset Y |
1568 /!\ * hg kill -n Y W Z |
1552 /!\ * hg kill -n Y W Z |
1569 """) |
1553 """) |
1570 if progresscb: progresscb() |
1554 if progresscb: progresscb() |
1571 tr = repo.transaction('stabilize-divergent') |
1555 tr = repo.transaction('stabilize-divergent') |
1572 try: |
1556 try: |
1573 repo.dirstate.setparents(divergent.node(), node.nullid) |
1557 repo.dirstate.setparents(divergent.node(), node.nullid) |
1574 oldlen = len(repo) |
1558 oldlen = len(repo) |
1575 amend(ui, repo, message='', logfile='') |
1559 amend(ui, repo, message='', logfile='') |
1576 if oldlen == len(repo): |
1560 if oldlen == len(repo): |
1577 new = divergent |
1561 new = divergent |
1578 # no changes |
1562 # no changes |
1579 else: |
1563 else: |
1580 new = repo['.'] |
1564 new = repo['.'] |
1581 createmarkers(repo, [(other, (new,))]) |
1565 createmarkers(repo, [(other, (new,))]) |
1582 retractboundary(repo, other.phase(), [new.node()]) |
1566 retractboundary(repo, other.phase(), [new.node()]) |
1583 tr.close() |
1567 tr.close() |
1584 finally: |
|
1585 tr.release() |
|
1586 finally: |
1568 finally: |
1587 lockmod.release(lock, wlock) |
1569 tr.release() |
1588 |
|
1589 |
1570 |
1590 def divergentdata(ctx): |
1571 def divergentdata(ctx): |
1591 """return base, other part of a conflict |
1572 """return base, other part of a conflict |
1592 |
1573 |
1593 This only return the first one. |
1574 This only return the first one. |