888 updatebookmarks(newid) |
896 updatebookmarks(newid) |
889 |
897 |
890 tr.close() |
898 tr.close() |
891 return newid, created |
899 return newid, created |
892 finally: |
900 finally: |
893 lockmod.release(lock, wlock, tr) |
901 lockmod.release(tr, lock, wlock) |
894 |
902 |
895 class MergeFailure(util.Abort): |
903 class MergeFailure(error.Abort): |
896 pass |
904 pass |
897 |
905 |
898 def relocate(repo, orig, dest, keepbranch=False): |
906 def relocate(repo, orig, dest, pctx=None, keepbranch=False): |
899 """rewrite <rev> on dest""" |
907 """rewrite <rev> on dest""" |
900 if orig.rev() == dest.rev(): |
908 if orig.rev() == dest.rev(): |
901 raise util.Abort(_('tried to relocate a node on top of itself'), |
909 raise error.Abort(_('tried to relocate a node on top of itself'), |
902 hint=_("This shouldn't happen. If you still " |
910 hint=_("This shouldn't happen. If you still " |
903 "need to move changesets, please do so " |
911 "need to move changesets, please do so " |
904 "manually with nothing to rebase - working " |
912 "manually with nothing to rebase - working " |
905 "directory parent is also destination")) |
913 "directory parent is also destination")) |
906 |
914 |
907 if not orig.p2().rev() == node.nullrev: |
915 if pctx is None: |
908 raise util.Abort( |
916 if len(orig.parents()) == 2: |
909 'no support for evolving merge changesets yet', |
917 raise error.Abort(_("tried to relocate a merge commit without " |
910 hint="Redo the merge and use `hg prune <old> --succ <new>` to obsolete the old one") |
918 "specifying which parent should be moved"), |
|
919 hint=_("Specify the parent by passing in pctx")) |
|
920 pctx = orig.p1() |
|
921 |
911 destbookmarks = repo.nodebookmarks(dest.node()) |
922 destbookmarks = repo.nodebookmarks(dest.node()) |
912 nodesrc = orig.node() |
923 nodesrc = orig.node() |
913 destphase = repo[nodesrc].phase() |
924 destphase = repo[nodesrc].phase() |
914 commitmsg = orig.description() |
925 commitmsg = orig.description() |
915 |
926 |
935 commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)]) |
946 commitmsg = commitmsg.replace(sha1, newsha1[:len(sha1)]) |
936 else: |
947 else: |
937 repo.ui.note(_('The stale commit message reference to %s could ' |
948 repo.ui.note(_('The stale commit message reference to %s could ' |
938 'not be updated\n') % sha1) |
949 'not be updated\n') % sha1) |
939 |
950 |
940 tr = repo.transaction('relocate') |
951 tr = repo.currenttransaction() |
|
952 assert tr is not None |
941 try: |
953 try: |
942 try: |
954 try: |
943 if repo['.'].rev() != dest.rev(): |
955 r = _evolvemerge(repo, orig, dest, pctx, keepbranch) |
944 merge.update(repo, dest, False, True, False) |
|
945 if bmactive(repo): |
|
946 repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo)) |
|
947 bmdeactivate(repo) |
|
948 if keepbranch: |
|
949 repo.dirstate.setbranch(orig.branch()) |
|
950 r = merge.graft(repo, orig, orig.p1(), ['local', 'graft']) |
|
951 if r[-1]: #some conflict |
956 if r[-1]: #some conflict |
952 raise util.Abort( |
957 raise error.Abort( |
953 'unresolved merge conflicts (see hg help resolve)') |
958 'unresolved merge conflicts (see hg help resolve)') |
954 if commitmsg is None: |
959 nodenew = _relocatecommit(repo, orig, commitmsg) |
955 commitmsg = orig.description() |
960 except error.Abort as exc: |
956 extra = dict(orig.extra()) |
|
957 if 'branch' in extra: |
|
958 del extra['branch'] |
|
959 extra['rebase_source'] = orig.hex() |
|
960 |
|
961 backup = repo.ui.backupconfig('phases', 'new-commit') |
|
962 try: |
|
963 targetphase = max(orig.phase(), phases.draft) |
|
964 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase') |
|
965 # Commit might fail if unresolved files exist |
|
966 nodenew = repo.commit(text=commitmsg, user=orig.user(), |
|
967 date=orig.date(), extra=extra) |
|
968 finally: |
|
969 repo.ui.restoreconfig(backup) |
|
970 except util.Abort, exc: |
|
971 repo.dirstate.beginparentchange() |
961 repo.dirstate.beginparentchange() |
972 repo.setparents(repo['.'].node(), nullid) |
962 repo.setparents(repo['.'].node(), nullid) |
973 writedirstate(repo.dirstate, tr) |
963 writedirstate(repo.dirstate, tr) |
974 # fix up dirstate for copies and renames |
964 # fix up dirstate for copies and renames |
975 copies.duplicatecopies(repo, dest.rev(), orig.p1().rev()) |
965 copies.duplicatecopies(repo, dest.rev(), orig.p1().rev()) |
976 repo.dirstate.endparentchange() |
966 repo.dirstate.endparentchange() |
977 class LocalMergeFailure(MergeFailure, exc.__class__): |
967 class LocalMergeFailure(MergeFailure, exc.__class__): |
978 pass |
968 pass |
979 exc.__class__ = LocalMergeFailure |
969 exc.__class__ = LocalMergeFailure |
|
970 tr.close() # to keep changes in this transaction (e.g. dirstate) |
980 raise |
971 raise |
981 oldbookmarks = repo.nodebookmarks(nodesrc) |
972 oldbookmarks = repo.nodebookmarks(nodesrc) |
982 if nodenew is not None: |
973 _finalizerelocate(repo, orig, dest, nodenew, tr) |
983 phases.retractboundary(repo, tr, destphase, [nodenew]) |
|
984 obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))]) |
|
985 for book in oldbookmarks: |
|
986 repo._bookmarks[book] = nodenew |
|
987 else: |
|
988 obsolete.createmarkers(repo, [(repo[nodesrc], ())]) |
|
989 # Behave like rebase, move bookmarks to dest |
|
990 for book in oldbookmarks: |
|
991 repo._bookmarks[book] = dest.node() |
|
992 for book in destbookmarks: # restore bookmark that rebase move |
|
993 repo._bookmarks[book] = dest.node() |
|
994 if oldbookmarks or destbookmarks: |
|
995 repo._bookmarks.recordchange(tr) |
|
996 tr.close() |
|
997 finally: |
974 finally: |
998 tr.release() |
975 pass # TODO: remove this redundant try/finally block |
999 return nodenew |
976 return nodenew |
1000 |
977 |
1001 def _bookmarksupdater(repo, oldid, tr): |
978 def _bookmarksupdater(repo, oldid, tr): |
1002 """Return a callable update(newid) updating the current bookmark |
979 """Return a callable update(newid) updating the current bookmark |
1003 and bookmarks bound to oldid to newid. |
980 and bookmarks bound to oldid to newid. |
1226 # same with parent data |
1203 # same with parent data |
1227 for c in pclustersmap.values(): |
1204 for c in pclustersmap.values(): |
1228 fc = (frozenset(c[0]), frozenset(c[1])) |
1205 fc = (frozenset(c[0]), frozenset(c[1])) |
1229 for n in fc[0]: |
1206 for n in fc[0]: |
1230 pclustersmap[n] = fc |
1207 pclustersmap[n] = fc |
1231 ui.write(' for known precursors: %9i\n' % known) |
1208 ui.write((' for known precursors: %9i\n' % known)) |
1232 ui.write(' with parents data: %9i\n' % parentsdata) |
1209 ui.write((' with parents data: %9i\n' % parentsdata)) |
1233 # successors data |
1210 # successors data |
1234 ui.write('markers with no successors: %9i\n' % sucscount[0]) |
1211 ui.write(('markers with no successors: %9i\n' % sucscount[0])) |
1235 ui.write(' 1 successors: %9i\n' % sucscount[1]) |
1212 ui.write((' 1 successors: %9i\n' % sucscount[1])) |
1236 ui.write(' 2 successors: %9i\n' % sucscount[2]) |
1213 ui.write((' 2 successors: %9i\n' % sucscount[2])) |
1237 ui.write(' more than 2 successors: %9i\n' % sucscount[3]) |
1214 ui.write((' more than 2 successors: %9i\n' % sucscount[3])) |
1238 # meta data info |
1215 # meta data info |
1239 ui.write(' available keys:\n') |
1216 ui.write((' available keys:\n')) |
1240 for key in sorted(metakeys): |
1217 for key in sorted(metakeys): |
1241 ui.write(' %15s: %9i\n' % (key, metakeys[key])) |
1218 ui.write((' %15s: %9i\n' % (key, metakeys[key]))) |
1242 |
1219 |
1243 allclusters = list(set(clustersmap.values())) |
1220 allclusters = list(set(clustersmap.values())) |
1244 allclusters.sort(key=lambda x: len(x[1])) |
1221 allclusters.sort(key=lambda x: len(x[1])) |
1245 ui.write('disconnected clusters: %9i\n' % len(allclusters)) |
1222 ui.write(('disconnected clusters: %9i\n' % len(allclusters))) |
1246 |
1223 |
1247 ui.write(' any known node: %9i\n' |
1224 ui.write(' any known node: %9i\n' |
1248 % len([c for c in allclusters |
1225 % len([c for c in allclusters |
1249 if [n for n in c[0] if nm.get(n) is not None]])) |
1226 if [n for n in c[0] if nm.get(n) is not None]])) |
1250 if allclusters: |
1227 if allclusters: |
1251 nbcluster = len(allclusters) |
1228 nbcluster = len(allclusters) |
1252 ui.write(' smallest length: %9i\n' % len(allclusters[0][1])) |
1229 ui.write((' smallest length: %9i\n' % len(allclusters[0][1]))) |
1253 ui.write(' longer length: %9i\n' % len(allclusters[-1][1])) |
1230 ui.write((' longer length: %9i\n' |
|
1231 % len(allclusters[-1][1]))) |
1254 median = len(allclusters[nbcluster//2][1]) |
1232 median = len(allclusters[nbcluster//2][1]) |
1255 ui.write(' median length: %9i\n' % median) |
1233 ui.write((' median length: %9i\n' % median)) |
1256 mean = sum(len(x[1]) for x in allclusters) // nbcluster |
1234 mean = sum(len(x[1]) for x in allclusters) // nbcluster |
1257 ui.write(' mean length: %9i\n' % mean) |
1235 ui.write((' mean length: %9i\n' % mean)) |
1258 allpclusters = list(set(pclustersmap.values())) |
1236 allpclusters = list(set(pclustersmap.values())) |
1259 allpclusters.sort(key=lambda x: len(x[1])) |
1237 allpclusters.sort(key=lambda x: len(x[1])) |
1260 ui.write(' using parents data: %9i\n' % len(allpclusters)) |
1238 ui.write((' using parents data: %9i\n' % len(allpclusters))) |
1261 ui.write(' any known node: %9i\n' |
1239 ui.write(' any known node: %9i\n' |
1262 % len([c for c in allclusters |
1240 % len([c for c in allclusters |
1263 if [n for n in c[0] if nm.get(n) is not None]])) |
1241 if [n for n in c[0] if nm.get(n) is not None]])) |
1264 if allpclusters: |
1242 if allpclusters: |
1265 nbcluster = len(allpclusters) |
1243 nbcluster = len(allpclusters) |
1266 ui.write(' smallest length: %9i\n' % len(allpclusters[0][1])) |
1244 ui.write((' smallest length: %9i\n' |
1267 ui.write(' longer length: %9i\n' % len(allpclusters[-1][1])) |
1245 % len(allpclusters[0][1]))) |
|
1246 ui.write((' longer length: %9i\n' |
|
1247 % len(allpclusters[-1][1]))) |
1268 median = len(allpclusters[nbcluster//2][1]) |
1248 median = len(allpclusters[nbcluster//2][1]) |
1269 ui.write(' median length: %9i\n' % median) |
1249 ui.write((' median length: %9i\n' % median)) |
1270 mean = sum(len(x[1]) for x in allpclusters) // nbcluster |
1250 mean = sum(len(x[1]) for x in allpclusters) // nbcluster |
1271 ui.write(' mean length: %9i\n' % mean) |
1251 ui.write((' mean length: %9i\n' % mean)) |
1272 |
1252 |
1273 def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category): |
1253 def _solveone(ui, repo, ctx, dryrun, confirm, progresscb, category): |
1274 """Resolve the troubles affecting one revision""" |
1254 """Resolve the troubles affecting one revision""" |
1275 wlock = lock = tr = None |
1255 wlock = lock = tr = None |
1276 try: |
1256 try: |
1532 @command('^evolve|stabilize|solve', |
1513 @command('^evolve|stabilize|solve', |
1533 [('n', 'dry-run', False, |
1514 [('n', 'dry-run', False, |
1534 _('do not perform actions, just print what would be done')), |
1515 _('do not perform actions, just print what would be done')), |
1535 ('', 'confirm', False, |
1516 ('', 'confirm', False, |
1536 _('ask for confirmation before performing the action')), |
1517 _('ask for confirmation before performing the action')), |
1537 ('A', 'any', False, _('also consider troubled changesets unrelated to current working directory')), |
1518 ('A', 'any', False, |
|
1519 _('also consider troubled changesets unrelated to current working ' |
|
1520 'directory')), |
1538 ('r', 'rev', [], _('solves troubles of these revisions')), |
1521 ('r', 'rev', [], _('solves troubles of these revisions')), |
1539 ('', 'bumped', False, _('solves only bumped changesets')), |
1522 ('', 'bumped', False, _('solves only bumped changesets')), |
1540 ('', 'divergent', False, _('solves only divergent changesets')), |
1523 ('', 'divergent', False, _('solves only divergent changesets')), |
1541 ('', 'unstable', False, _('solves only unstable changesets (default)')), |
1524 ('', 'unstable', False, _('solves only unstable changesets (default)')), |
1542 ('a', 'all', False, _('evolve all troubled changesets related to the current ' |
1525 ('a', 'all', False, _('evolve all troubled changesets related to the ' |
1543 'working directory and its descendants')), |
1526 'current working directory and its descendants')), |
1544 ('c', 'continue', False, _('continue an interrupted evolution')), |
1527 ('c', 'continue', False, _('continue an interrupted evolution')), |
1545 ] + mergetoolopts, |
1528 ] + mergetoolopts, |
1546 _('[OPTIONS]...')) |
1529 _('[OPTIONS]...')) |
1547 def evolve(ui, repo, **opts): |
1530 def evolve(ui, repo, **opts): |
1548 """solve troubled changesets in your repository |
1531 """solve troubled changesets in your repository |
1549 |
1532 |
1550 Modifying history can lead to various types of troubled changesets: unstable, |
1533 Modifying history can lead to various types of troubled changesets: |
1551 bumped, or divergent. The evolve command resolves your troubles by executing one |
1534 unstable, bumped, or divergent. The evolve command resolves your troubles |
1552 of the following actions: |
1535 by executing one of the following actions: |
1553 |
1536 |
1554 - update working copy to a successor |
1537 - update working copy to a successor |
1555 - rebase an unstable changeset |
1538 - rebase an unstable changeset |
1556 - extract the desired changes from a bumped changeset |
1539 - extract the desired changes from a bumped changeset |
1557 - fuse divergent changesets back together |
1540 - fuse divergent changesets back together |
1558 |
1541 |
1559 If you pass no arguments, evolve works in automatic mode: it will execute a |
1542 If you pass no arguments, evolve works in automatic mode: it will execute a |
1560 single action to reduce instability related to your working copy. There are two |
1543 single action to reduce instability related to your working copy. There are |
1561 cases for this action. First, if the parent of your working copy is obsolete, |
1544 two cases for this action. First, if the parent of your working copy is |
1562 evolve updates to the parent's successor. Second, if the working copy parent is |
1545 obsolete, evolve updates to the parent's successor. Second, if the working |
1563 not obsolete but has obsolete predecessors, then evolve determines if there is an |
1546 copy parent is not obsolete but has obsolete predecessors, then evolve |
1564 unstable changeset that can be rebased onto the working copy parent in order to |
1547 determines if there is an unstable changeset that can be rebased onto the |
1565 reduce instability. If so, evolve rebases that changeset. If not, evolve refuses |
1548 working copy parent in order to reduce instability. |
1566 to guess your intention, and gives a hint about what you might want to do next. |
1549 If so, evolve rebases that changeset. If not, evolve refuses to guess your |
|
1550 intention, and gives a hint about what you might want to do next. |
1567 |
1551 |
1568 Any time evolve creates a changeset, it updates the working copy to the new |
1552 Any time evolve creates a changeset, it updates the working copy to the new |
1569 changeset. (Currently, every successful evolve operation involves an update as |
1553 changeset. (Currently, every successful evolve operation involves an update |
1570 well; this may change in future.) |
1554 as well; this may change in future.) |
1571 |
1555 |
1572 Automatic mode only handles common use cases. For example, it avoids taking |
1556 Automatic mode only handles common use cases. For example, it avoids taking |
1573 action in the case of ambiguity, and it ignores unstable changesets that are not |
1557 action in the case of ambiguity, and it ignores unstable changesets that |
1574 related to your working copy. It also refuses to solve bumped or divergent |
1558 are not related to your working copy. |
1575 changesets unless you explicity request such behavior (see below). |
1559 It also refuses to solve bumped or divergent changesets unless you explicity |
|
1560 request such behavior (see below). |
1576 |
1561 |
1577 Eliminating all instability around your working copy may require multiple |
1562 Eliminating all instability around your working copy may require multiple |
1578 invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively select and |
1563 invocations of :hg:`evolve`. Alternately, use ``--all`` to recursively |
1579 evolve all unstable changesets that can be rebased onto the working copy parent. |
1564 select and evolve all unstable changesets that can be rebased onto the |
|
1565 working copy parent. |
1580 This is more powerful than successive invocations, since ``--all`` handles |
1566 This is more powerful than successive invocations, since ``--all`` handles |
1581 ambiguous cases (e.g. unstable changesets with multiple children) by evolving all |
1567 ambiguous cases (e.g. unstable changesets with multiple children) by |
1582 branches. |
1568 evolving all branches. |
1583 |
1569 |
1584 When your repository cannot be handled by automatic mode, you might need to use |
1570 When your repository cannot be handled by automatic mode, you might need to |
1585 ``--rev`` to specify a changeset to evolve. For example, if you have an unstable |
1571 use ``--rev`` to specify a changeset to evolve. For example, if you have |
1586 changeset that is not related to the working copy parent, you could use ``--rev`` |
1572 an unstable changeset that is not related to the working copy parent, |
1587 to evolve it. Or, if some changeset has multiple unstable children, evolve in |
1573 you could use ``--rev`` to evolve it. Or, if some changeset has multiple |
1588 automatic mode refuses to guess which one to evolve; you have to use ``--rev`` |
1574 unstable children, evolve in automatic mode refuses to guess which one to |
1589 in that case. |
1575 evolve; you have to use ``--rev`` in that case. |
1590 |
1576 |
1591 Alternately, ``--any`` makes evolve search for the next evolvable changeset |
1577 Alternately, ``--any`` makes evolve search for the next evolvable changeset |
1592 regardless of whether it is related to the working copy parent. |
1578 regardless of whether it is related to the working copy parent. |
1593 |
1579 |
1594 You can supply multiple revisions to evolve multiple troubled changesets in a |
1580 You can supply multiple revisions to evolve multiple troubled changesets |
1595 single invocation. In revset terms, ``--any`` is equivalent to ``--rev |
1581 in a single invocation. In revset terms, ``--any`` is equivalent to ``--rev |
1596 first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are |
1582 first(unstable())``. ``--rev`` and ``--all`` are mutually exclusive, as are |
1597 ``--rev`` and ``--any``. |
1583 ``--rev`` and ``--any``. |
1598 |
1584 |
1599 ``hg evolve --any --all`` is useful for cleaning up instability across all |
1585 ``hg evolve --any --all`` is useful for cleaning up instability across all |
1600 branches, letting evolve figure out the appropriate order and destination. |
1586 branches, letting evolve figure out the appropriate order and destination. |
1601 |
1587 |
1602 When you have troubled changesets that are not unstable, :hg:`evolve` refuses to |
1588 When you have troubled changesets that are not unstable, :hg:`evolve` |
1603 consider them unless you specify the category of trouble you wish to resolve, |
1589 refuses to consider them unless you specify the category of trouble you |
1604 with ``--bumped`` or ``--divergent``. These options are currently mutually |
1590 wish to resolve, with ``--bumped`` or ``--divergent``. These options are |
1605 exclusive with each other and with ``--unstable`` (the default). You can combine |
1591 currently mutually exclusive with each other and with ``--unstable`` |
1606 ``--bumped`` or ``--divergent`` with ``--rev``, ``--all``, or ``--any``. |
1592 (the default). You can combine ``--bumped`` or ``--divergent`` with |
|
1593 ``--rev``, ``--all``, or ``--any``. |
1607 |
1594 |
1608 """ |
1595 """ |
1609 |
1596 |
1610 # Options |
1597 # Options |
1611 contopt = opts['continue'] |
1598 contopt = opts['continue'] |
1655 count = allopt and len(troubled) or 1 |
1643 count = allopt and len(troubled) or 1 |
1656 showprogress = allopt |
1644 showprogress = allopt |
1657 |
1645 |
1658 def progresscb(): |
1646 def progresscb(): |
1659 if revopt or allopt: |
1647 if revopt or allopt: |
1660 ui.progress('evolve', seen, unit='changesets', total=count) |
1648 ui.progress(_('evolve'), seen, unit='changesets', total=count) |
1661 |
1649 |
1662 # Continuation handling |
1650 # Continuation handling |
1663 if contopt: |
1651 if contopt: |
1664 if anyopt: |
1652 if anyopt: |
1665 raise util.Abort('cannot specify both "--any" and "--continue"') |
1653 raise error.Abort('cannot specify both "--any" and "--continue"') |
1666 if allopt: |
1654 if allopt: |
1667 raise util.Abort('cannot specify both "--all" and "--continue"') |
1655 raise error.Abort('cannot specify both "--all" and "--continue"') |
1668 graftcmd = commands.table['graft'][0] |
1656 state = _evolvestateread(repo) |
1669 return graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) |
1657 if state is None: |
|
1658 raise error.Abort('no evolve to continue') |
|
1659 orig = repo[state['current']] |
|
1660 # XXX This is a terrible terrible hack, please get rid of it. |
|
1661 repo.opener.write('graftstate', orig.hex() + '\n') |
|
1662 try: |
|
1663 graftcmd = commands.table['graft'][0] |
|
1664 ret = graftcmd(ui, repo, old_obsolete=True, **{'continue': True}) |
|
1665 _evolvestatedelete(repo) |
|
1666 return ret |
|
1667 finally: |
|
1668 util.unlinkpath(repo.join('graftstate'), ignoremissing=True) |
1670 cmdutil.bailifchanged(repo) |
1669 cmdutil.bailifchanged(repo) |
1671 |
1670 |
1672 |
1671 |
1673 if revopt and allopt: |
1672 if revopt and allopt: |
1674 raise util.Abort('cannot specify both "--rev" and "--all"') |
1673 raise error.Abort('cannot specify both "--rev" and "--all"') |
1675 if revopt and anyopt: |
1674 if revopt and anyopt: |
1676 raise util.Abort('cannot specify both "--rev" and "--any"') |
1675 raise error.Abort('cannot specify both "--rev" and "--any"') |
1677 |
1676 |
1678 revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat) |
1677 revs = _selectrevs(repo, allopt, revopt, anyopt, targetcat) |
1679 |
1678 |
1680 if not revs: |
1679 if not revs: |
1681 return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat) |
1680 return _handlenotrouble(ui, repo, allopt, revopt, anyopt, targetcat) |
1747 return sorted(result - target) |
1746 return sorted(result - target) |
1748 |
1747 |
1749 def _solveunstable(ui, repo, orig, dryrun=False, confirm=False, |
1748 def _solveunstable(ui, repo, orig, dryrun=False, confirm=False, |
1750 progresscb=None): |
1749 progresscb=None): |
1751 """Stabilize an unstable changeset""" |
1750 """Stabilize an unstable changeset""" |
1752 obs = orig.parents()[0] |
1751 pctx = orig.p1() |
1753 if not obs.obsolete() and len(orig.parents()) == 2: |
1752 if len(orig.parents()) == 2: |
1754 obs = orig.parents()[1] # second parent is obsolete ? |
1753 if not pctx.obsolete(): |
1755 |
1754 pctx = orig.p2() # second parent is obsolete ? |
1756 if not obs.obsolete(): |
1755 elif orig.p2().obsolete(): |
1757 ui.warn("cannot solve instability of %s, skipping\n" % orig) |
1756 raise error.Abort(_("no support for evolving merge changesets " |
|
1757 "with two obsolete parents yet"), |
|
1758 hint=_("Redo the merge and use `hg prune <old> " |
|
1759 "--succ <new>` to obsolete the old one")) |
|
1760 |
|
1761 if not pctx.obsolete(): |
|
1762 ui.warn(_("cannot solve instability of %s, skipping\n") % orig) |
1758 return False |
1763 return False |
|
1764 obs = pctx |
1759 newer = obsolete.successorssets(repo, obs.node()) |
1765 newer = obsolete.successorssets(repo, obs.node()) |
1760 # search of a parent which is not killed |
1766 # search of a parent which is not killed |
1761 while not newer or newer == [()]: |
1767 while not newer or newer == [()]: |
1762 ui.debug("stabilize target %s is plain dead," |
1768 ui.debug("stabilize target %s is plain dead," |
1763 " trying to stabilize on its parent\n" % |
1769 " trying to stabilize on its parent\n" % |
1764 obs) |
1770 obs) |
1765 obs = obs.parents()[0] |
1771 obs = obs.parents()[0] |
1766 newer = obsolete.successorssets(repo, obs.node()) |
1772 newer = obsolete.successorssets(repo, obs.node()) |
1767 if len(newer) > 1: |
1773 if len(newer) > 1: |
1768 msg = _("skipping %s: divergent rewriting. can't choose destination\n") % obs |
1774 msg = _("skipping %s: divergent rewriting. can't choose " |
|
1775 "destination\n") % obs |
1769 ui.write_err(msg) |
1776 ui.write_err(msg) |
1770 return 2 |
1777 return 2 |
1771 targets = newer[0] |
1778 targets = newer[0] |
1772 assert targets |
1779 assert targets |
1773 if len(targets) > 1: |
1780 if len(targets) > 1: |
1820 ui.write_err(msg) |
1827 ui.write_err(msg) |
1821 return 2 |
1828 return 2 |
1822 prec = repo.set('last(allprecursors(%d) and public())', bumped).next() |
1829 prec = repo.set('last(allprecursors(%d) and public())', bumped).next() |
1823 # For now we deny target merge |
1830 # For now we deny target merge |
1824 if len(prec.parents()) > 1: |
1831 if len(prec.parents()) > 1: |
1825 msg = _('skipping: %s: public version is a merge, this not handled yet\n') % prec |
1832 msg = _('skipping: %s: public version is a merge, ' |
|
1833 'this is not handled yet\n') % prec |
1826 ui.write_err(msg) |
1834 ui.write_err(msg) |
1827 return 2 |
1835 return 2 |
1828 |
1836 |
1829 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1837 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1830 if not ui.quiet or confirm: |
1838 if not ui.quiet or confirm: |
1831 repo.ui.write(_('recreate:')) |
1839 repo.ui.write(_('recreate:')) |
1832 displayer.show(bumped) |
1840 displayer.show(bumped) |
1833 repo.ui.write(_('atop:')) |
1841 repo.ui.write(_('atop:')) |
1834 displayer.show(prec) |
1842 displayer.show(prec) |
1835 if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y': |
1843 if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y': |
1836 raise util.Abort(_('evolve aborted by user')) |
1844 raise error.Abort(_('evolve aborted by user')) |
1837 if dryrun: |
1845 if dryrun: |
1838 todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1()) |
1846 todo = 'hg rebase --rev %s --dest %s;\n' % (bumped, prec.p1()) |
1839 repo.ui.write(todo) |
1847 repo.ui.write(todo) |
1840 repo.ui.write('hg update %s;\n' % prec) |
1848 repo.ui.write(('hg update %s;\n' % prec)) |
1841 repo.ui.write('hg revert --all --rev %s;\n' % bumped) |
1849 repo.ui.write(('hg revert --all --rev %s;\n' % bumped)) |
1842 repo.ui.write('hg commit --msg "bumped update to %s"') |
1850 repo.ui.write(('hg commit --msg "bumped update to %s"')) |
1843 return 0 |
1851 return 0 |
1844 if progresscb: progresscb() |
1852 if progresscb: progresscb() |
1845 newid = tmpctx = None |
1853 newid = tmpctx = None |
1846 tmpctx = bumped |
1854 tmpctx = bumped |
1847 # Basic check for common parent. Far too complicated and fragile |
1855 # Basic check for common parent. Far too complicated and fragile |
1848 tr = repo.transaction('bumped-stabilize') |
1856 tr = repo.currenttransaction() |
|
1857 assert tr is not None |
1849 bmupdate = _bookmarksupdater(repo, bumped.node(), tr) |
1858 bmupdate = _bookmarksupdater(repo, bumped.node(), tr) |
1850 try: |
1859 try: |
1851 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1860 if not list(repo.set('parents(%d) and parents(%d)', bumped, prec)): |
1852 # Need to rebase the changeset at the right place |
1861 # Need to rebase the changeset at the right place |
1853 repo.ui.status( |
1862 repo.ui.status( |
1925 repo = repo.unfiltered() |
1933 repo = repo.unfiltered() |
1926 divergent = repo[divergent.rev()] |
1934 divergent = repo[divergent.rev()] |
1927 base, others = divergentdata(divergent) |
1935 base, others = divergentdata(divergent) |
1928 if len(others) > 1: |
1936 if len(others) > 1: |
1929 othersstr = "[%s]" % (','.join([str(i) for i in others])) |
1937 othersstr = "[%s]" % (','.join([str(i) for i in others])) |
1930 msg = _("skipping %d:divergent with a changeset that got splitted into multiple ones:\n" |
1938 msg = _("skipping %d:divergent with a changeset that got splitted" |
|
1939 " into multiple ones:\n" |
1931 "|[%s]\n" |
1940 "|[%s]\n" |
1932 "| This is not handled by automatic evolution yet\n" |
1941 "| This is not handled by automatic evolution yet\n" |
1933 "| You have to fallback to manual handling with commands " |
1942 "| You have to fallback to manual handling with commands " |
1934 "such as:\n" |
1943 "such as:\n" |
1935 "| - hg touch -D\n" |
1944 "| - hg touch -D\n" |
1936 "| - hg prune\n" |
1945 "| - hg prune\n" |
1937 "| \n" |
1946 "| \n" |
1938 "| You should contact your local evolution Guru for help.\n" |
1947 "| You should contact your local evolution Guru for help.\n" |
1939 % (divergent, othersstr)) |
1948 ) % (divergent, othersstr) |
1940 ui.write_err(msg) |
1949 ui.write_err(msg) |
1941 return 2 |
1950 return 2 |
1942 other = others[0] |
1951 other = others[0] |
1943 if len(other.parents()) > 1: |
1952 if len(other.parents()) > 1: |
1944 msg = _("skipping %s: divergent changeset can't be a merge (yet)\n" % divergent) |
1953 msg = _("skipping %s: divergent changeset can't be " |
|
1954 "a merge (yet)\n") % divergent |
1945 ui.write_err(msg) |
1955 ui.write_err(msg) |
1946 hint = _("You have to fallback to solving this by hand...\n" |
1956 hint = _("You have to fallback to solving this by hand...\n" |
1947 "| This probably means redoing the merge and using \n" |
1957 "| This probably means redoing the merge and using \n" |
1948 "| `hg prune` to kill older version.\n") |
1958 "| `hg prune` to kill older version.\n") |
1949 ui.write_err(hint) |
1959 ui.write_err(hint) |
1950 return 2 |
1960 return 2 |
1951 if other.p1() not in divergent.parents(): |
1961 if other.p1() not in divergent.parents(): |
1952 msg = _("skipping %s: have a different parent than %s (not handled yet)\n") % (divergent, other) |
1962 msg = _("skipping %s: have a different parent than %s " |
|
1963 "(not handled yet)\n") % (divergent, other) |
1953 hint = _("| %(d)s, %(o)s are not based on the same changeset.\n" |
1964 hint = _("| %(d)s, %(o)s are not based on the same changeset.\n" |
1954 "| With the current state of its implementation, \n" |
1965 "| With the current state of its implementation, \n" |
1955 "| evolve does not work in that case.\n" |
1966 "| evolve does not work in that case.\n" |
1956 "| rebase one of them next to the other and run \n" |
1967 "| rebase one of them next to the other and run \n" |
1957 "| this command again.\n" |
1968 "| this command again.\n" |
1958 "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" |
1969 "| - either: hg rebase --dest 'p1(%(d)s)' -r %(o)s\n" |
1959 "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n" |
1970 "| - or: hg rebase --dest 'p1(%(o)s)' -r %(d)s\n" |
1960 % {'d': divergent, 'o': other}) |
1971 ) % {'d': divergent, 'o': other} |
1961 ui.write_err(msg) |
1972 ui.write_err(msg) |
1962 ui.write_err(hint) |
1973 ui.write_err(hint) |
1963 return 2 |
1974 return 2 |
1964 |
1975 |
1965 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1976 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
1969 ui.write(_('with: ')) |
1980 ui.write(_('with: ')) |
1970 displayer.show(other) |
1981 displayer.show(other) |
1971 ui.write(_('base: ')) |
1982 ui.write(_('base: ')) |
1972 displayer.show(base) |
1983 displayer.show(base) |
1973 if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y': |
1984 if confirm and ui.prompt(_('perform evolve? [Ny]'), 'n') != 'y': |
1974 raise util.Abort(_('evolve aborted by user')) |
1985 raise error.Abort(_('evolve aborted by user')) |
1975 if dryrun: |
1986 if dryrun: |
1976 ui.write('hg update -c %s &&\n' % divergent) |
1987 ui.write(('hg update -c %s &&\n' % divergent)) |
1977 ui.write('hg merge %s &&\n' % other) |
1988 ui.write(('hg merge %s &&\n' % other)) |
1978 ui.write('hg commit -m "auto merge resolving conflict between ' |
1989 ui.write(('hg commit -m "auto merge resolving conflict between ' |
1979 '%s and %s"&&\n' % (divergent, other)) |
1990 '%s and %s"&&\n' % (divergent, other))) |
1980 ui.write('hg up -C %s &&\n' % base) |
1991 ui.write(('hg up -C %s &&\n' % base)) |
1981 ui.write('hg revert --all --rev tip &&\n') |
1992 ui.write(('hg revert --all --rev tip &&\n')) |
1982 ui.write('hg commit -m "`hg log -r %s --template={desc}`";\n' |
1993 ui.write(('hg commit -m "`hg log -r %s --template={desc}`";\n' |
1983 % divergent) |
1994 % divergent)) |
1984 return |
1995 return |
1985 if divergent not in repo[None].parents(): |
1996 if divergent not in repo[None].parents(): |
1986 repo.ui.status(_('updating to "local" conflict\n')) |
1997 repo.ui.status(_('updating to "local" conflict\n')) |
1987 hg.update(repo, divergent.rev()) |
1998 hg.update(repo, divergent.rev()) |
1988 repo.ui.note(_('merging divergent changeset\n')) |
1999 repo.ui.note(_('merging divergent changeset\n')) |
1989 if progresscb: progresscb() |
2000 if progresscb: progresscb() |
1990 stats = merge.update(repo, |
2001 if 'partial' in merge.update.__doc__: |
1991 other.node(), |
2002 # Mercurial < 43c00ca887d1 (3.7) |
1992 branchmerge=True, |
2003 stats = merge.update(repo, |
1993 force=False, |
2004 other.node(), |
1994 partial=None, |
2005 branchmerge=True, |
1995 ancestor=base.node(), |
2006 force=False, |
1996 mergeancestor=True) |
2007 partial=None, |
|
2008 ancestor=base.node(), |
|
2009 mergeancestor=True) |
|
2010 else: |
|
2011 stats = merge.update(repo, |
|
2012 other.node(), |
|
2013 branchmerge=True, |
|
2014 force=False, |
|
2015 ancestor=base.node(), |
|
2016 mergeancestor=True) |
|
2017 |
1997 hg._showstats(repo, stats) |
2018 hg._showstats(repo, stats) |
1998 if stats[3]: |
2019 if stats[3]: |
1999 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " |
2020 repo.ui.status(_("use 'hg resolve' to retry unresolved file merges " |
2000 "or 'hg update -C .' to abandon\n")) |
2021 "or 'hg update -C .' to abandon\n")) |
2001 if stats[3] > 0: |
2022 if stats[3] > 0: |
2002 raise util.Abort('merge conflict between several amendments ' |
2023 raise error.Abort('merge conflict between several amendments ' |
2003 '(this is not automated yet)', |
2024 '(this is not automated yet)', |
2004 hint="""/!\ You can try: |
2025 hint="""/!\ You can try: |
2005 /!\ * manual merge + resolve => new cset X |
2026 /!\ * manual merge + resolve => new cset X |
2006 /!\ * hg up to the parent of the amended changeset (which are named W and Z) |
2027 /!\ * hg up to the parent of the amended changeset (which are named W and Z) |
2007 /!\ * hg revert --all -r X |
2028 /!\ * hg revert --all -r X |
2008 /!\ * hg ci -m "same message as the amended changeset" => new cset Y |
2029 /!\ * hg ci -m "same message as the amended changeset" => new cset Y |
2009 /!\ * hg kill -n Y W Z |
2030 /!\ * hg kill -n Y W Z |
2010 """) |
2031 """) |
2011 if progresscb: progresscb() |
2032 if progresscb: progresscb() |
2012 tr = repo.transaction('stabilize-divergent') |
2033 emtpycommitallowed = repo.ui.backupconfig('ui', 'allowemptycommit') |
|
2034 tr = repo.currenttransaction() |
|
2035 assert tr is not None |
2013 try: |
2036 try: |
|
2037 repo.ui.setconfig('ui', 'allowemptycommit', True) |
2014 repo.dirstate.beginparentchange() |
2038 repo.dirstate.beginparentchange() |
2015 repo.dirstate.setparents(divergent.node(), node.nullid) |
2039 repo.dirstate.setparents(divergent.node(), node.nullid) |
2016 repo.dirstate.endparentchange() |
2040 repo.dirstate.endparentchange() |
2017 oldlen = len(repo) |
2041 oldlen = len(repo) |
2018 amend(ui, repo, message='', logfile='') |
2042 amend(ui, repo, message='', logfile='') |
2039 newer = obsolete.successorssets(ctx._repo, base.node()) |
2062 newer = obsolete.successorssets(ctx._repo, base.node()) |
2040 # drop filter and solution including the original ctx |
2063 # drop filter and solution including the original ctx |
2041 newer = [n for n in newer if n and ctx.node() not in n] |
2064 newer = [n for n in newer if n and ctx.node() not in n] |
2042 if newer: |
2065 if newer: |
2043 return base, tuple(ctx._repo[o] for o in newer[0]) |
2066 return base, tuple(ctx._repo[o] for o in newer[0]) |
2044 raise util.Abort("base of divergent changeset %s not found" % ctx, |
2067 raise error.Abort("base of divergent changeset %s not found" % ctx, |
2045 hint='this case is not yet handled') |
2068 hint='this case is not yet handled') |
2046 |
2069 |
2047 |
2070 |
2048 |
2071 |
2049 shorttemplate = '[{rev}] {desc|firstline}\n' |
2072 shorttemplate = '[{rev}] {desc|firstline}\n' |
2050 |
2073 |
2051 @command('^previous', |
2074 @command('^previous', |
2052 [('B', 'move-bookmark', False, |
2075 [('B', 'move-bookmark', False, |
2053 _('move active bookmark after update')), |
2076 _('move active bookmark after update')), |
2054 ('', 'merge', False, _('bring uncommitted change along')), |
2077 ('', 'merge', False, _('bring uncommitted change along')), |
2055 ('n', 'dry-run', False, _('do not perform actions, just print what would be done'))], |
2078 ('n', 'dry-run', False, |
|
2079 _('do not perform actions, just print what would be done'))], |
2056 '[OPTION]...') |
2080 '[OPTION]...') |
2057 def cmdprevious(ui, repo, **opts): |
2081 def cmdprevious(ui, repo, **opts): |
2058 """update to parent revision |
2082 """update to parent revision |
2059 |
2083 |
2060 Displays the summary line of the destination for clarity.""" |
2084 Displays the summary line of the destination for clarity.""" |
2061 wkctx = repo[None] |
2085 wkctx = repo[None] |
2062 wparents = wkctx.parents() |
2086 wparents = wkctx.parents() |
2063 dryrunopt = opts['dry_run'] |
2087 dryrunopt = opts['dry_run'] |
2064 if len(wparents) != 1: |
2088 if len(wparents) != 1: |
2065 raise util.Abort('merge in progress') |
2089 raise error.Abort('merge in progress') |
2066 if not opts['merge']: |
2090 if not opts['merge']: |
2067 try: |
2091 try: |
2068 cmdutil.bailifchanged(repo) |
2092 cmdutil.bailifchanged(repo) |
2069 except error.Abort, exc: |
2093 except error.Abort as exc: |
2070 exc.hint = _('do you want --merge?') |
2094 exc.hint = _('do you want --merge?') |
2071 raise |
2095 raise |
2072 |
2096 |
2073 parents = wparents[0].parents() |
2097 parents = wparents[0].parents() |
2074 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2098 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2075 if len(parents) == 1: |
2099 if len(parents) == 1: |
2076 p = parents[0] |
2100 p = parents[0] |
2077 bm = bmactive(repo) |
2101 bm = bmactive(repo) |
2078 shouldmove = opts.get('move_bookmark') and bm is not None |
2102 shouldmove = opts.get('move_bookmark') and bm is not None |
2079 if dryrunopt: |
2103 if dryrunopt: |
2080 ui.write('hg update %s;\n' % p.rev()) |
2104 ui.write(('hg update %s;\n' % p.rev())) |
2081 if shouldmove: |
2105 if shouldmove: |
2082 ui.write('hg bookmark %s -r %s;\n' % (bm, p.rev())) |
2106 ui.write(('hg bookmark %s -r %s;\n' % (bm, p.rev()))) |
2083 else: |
2107 else: |
2084 ret = hg.update(repo, p.rev()) |
2108 ret = hg.update(repo, p.rev()) |
2085 if not ret: |
2109 if not ret: |
|
2110 tr = lock = None |
2086 wlock = repo.wlock() |
2111 wlock = repo.wlock() |
2087 try: |
2112 try: |
|
2113 lock = repo.lock() |
|
2114 tr = repo.transaction('previous') |
2088 if shouldmove: |
2115 if shouldmove: |
2089 repo._bookmarks[bm] = p.node() |
2116 repo._bookmarks[bm] = p.node() |
2090 repo._bookmarks.write() |
2117 repo._bookmarks.recordchange(tr) |
2091 else: |
2118 else: |
2092 bmdeactivate(repo) |
2119 bmdeactivate(repo) |
|
2120 tr.close() |
2093 finally: |
2121 finally: |
2094 wlock.release() |
2122 lockmod.release(tr, lock, wlock) |
2095 displayer.show(p) |
2123 displayer.show(p) |
2096 return 0 |
2124 return 0 |
2097 else: |
2125 else: |
2098 for p in parents: |
2126 for p in parents: |
2099 displayer.show(p) |
2127 displayer.show(p) |
2116 """ |
2145 """ |
2117 wkctx = repo[None] |
2146 wkctx = repo[None] |
2118 wparents = wkctx.parents() |
2147 wparents = wkctx.parents() |
2119 dryrunopt = opts['dry_run'] |
2148 dryrunopt = opts['dry_run'] |
2120 if len(wparents) != 1: |
2149 if len(wparents) != 1: |
2121 raise util.Abort('merge in progress') |
2150 raise error.Abort('merge in progress') |
2122 if not opts['merge']: |
2151 if not opts['merge']: |
2123 try: |
2152 try: |
2124 cmdutil.bailifchanged(repo) |
2153 cmdutil.bailifchanged(repo) |
2125 except error.Abort, exc: |
2154 except error.Abort as exc: |
2126 exc.hint = _('do you want --merge?') |
2155 exc.hint = _('do you want --merge?') |
2127 raise |
2156 raise |
2128 |
2157 |
2129 children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()] |
2158 children = [ctx for ctx in wparents[0].children() if not ctx.obsolete()] |
2130 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2159 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2131 if len(children) == 1: |
2160 if len(children) == 1: |
2132 c = children[0] |
2161 c = children[0] |
2133 bm = bmactive(repo) |
2162 bm = bmactive(repo) |
2134 shouldmove = opts.get('move_bookmark') and bm is not None |
2163 shouldmove = opts.get('move_bookmark') and bm is not None |
2135 if dryrunopt: |
2164 if dryrunopt: |
2136 ui.write('hg update %s;\n' % c.rev()) |
2165 ui.write(('hg update %s;\n' % c.rev())) |
2137 if shouldmove: |
2166 if shouldmove: |
2138 ui.write('hg bookmark %s -r %s;\n' % (bm, c.rev())) |
2167 ui.write(('hg bookmark %s -r %s;\n' % (bm, c.rev()))) |
2139 else: |
2168 else: |
2140 ret = hg.update(repo, c.rev()) |
2169 ret = hg.update(repo, c.rev()) |
2141 if not ret: |
2170 if not ret: |
|
2171 lock = tr = None |
2142 wlock = repo.wlock() |
2172 wlock = repo.wlock() |
2143 try: |
2173 try: |
|
2174 lock = repo.lock() |
|
2175 tr = repo.transaction('next') |
2144 if shouldmove: |
2176 if shouldmove: |
2145 repo._bookmarks[bm] = c.node() |
2177 repo._bookmarks[bm] = c.node() |
2146 repo._bookmarks.write() |
2178 repo._bookmarks.recordchange(tr) |
2147 else: |
2179 else: |
2148 bmdeactivate(repo) |
2180 bmdeactivate(repo) |
|
2181 tr.close() |
2149 finally: |
2182 finally: |
2150 wlock.release() |
2183 lockmod.release(tr, lock, wlock) |
2151 displayer.show(c) |
2184 displayer.show(c) |
2152 result = 0 |
2185 result = 0 |
2153 elif children: |
2186 elif children: |
2154 ui.warn("ambigious next changeset:\n") |
2187 ui.warn(_("ambigious next changeset:\n")) |
2155 for c in children: |
2188 for c in children: |
2156 displayer.show(c) |
2189 displayer.show(c) |
2157 ui.warn(_('explicitly update to one of them\n')) |
2190 ui.warn(_('explicitly update to one of them\n')) |
2158 result = 1 |
2191 result = 1 |
2159 else: |
2192 else: |
2179 ui.status(_('working directory now at %s\n') % repo['.']) |
2212 ui.status(_('working directory now at %s\n') % repo['.']) |
2180 return result |
2213 return result |
2181 return 1 |
2214 return 1 |
2182 return result |
2215 return result |
2183 |
2216 |
2184 def _reachablefrombookmark(repo, revs, mark): |
2217 def _reachablefrombookmark(repo, revs, bookmarks): |
2185 """filter revisions and bookmarks reachable from the given bookmark |
2218 """filter revisions and bookmarks reachable from the given bookmark |
2186 yoinked from mq.py |
2219 yoinked from mq.py |
2187 """ |
2220 """ |
2188 marks = repo._bookmarks |
2221 repomarks = repo._bookmarks |
2189 if mark not in marks: |
2222 if not bookmarks.issubset(repomarks): |
2190 raise util.Abort(_("bookmark '%s' not found") % mark) |
2223 raise error.Abort(_("bookmark '%s' not found") % |
|
2224 ','.join(sorted(bookmarks - set(repomarks.keys())))) |
2191 |
2225 |
2192 # If the requested bookmark is not the only one pointing to a |
2226 # If the requested bookmark is not the only one pointing to a |
2193 # a revision we have to only delete the bookmark and not strip |
2227 # a revision we have to only delete the bookmark and not strip |
2194 # anything. revsets cannot detect that case. |
2228 # anything. revsets cannot detect that case. |
2195 uniquebm = True |
2229 nodetobookmarks = {} |
2196 for m, n in marks.iteritems(): |
2230 for mark, node in repomarks.iteritems(): |
2197 if m != mark and n == repo[mark].node(): |
2231 nodetobookmarks.setdefault(node, []).append(mark) |
2198 uniquebm = False |
2232 for marks in nodetobookmarks.values(): |
2199 break |
2233 if bookmarks.issuperset(marks): |
2200 if uniquebm: |
2234 if util.safehasattr(repair, 'stripbmrevset'): |
2201 if util.safehasattr(repair, 'stripbmrevset'): |
2235 rsrevs = repair.stripbmrevset(repo, marks[0]) |
2202 rsrevs = repair.stripbmrevset(repo, mark) |
2236 else: |
2203 else: |
2237 rsrevs = repo.revs("ancestors(bookmark(%s)) - " |
2204 rsrevs = repo.revs("ancestors(bookmark(%s)) - " |
2238 "ancestors(head() and not bookmark(%s)) - " |
2205 "ancestors(head() and not bookmark(%s)) - " |
2239 "ancestors(bookmark() and not bookmark(%s)) - " |
2206 "ancestors(bookmark() and not bookmark(%s)) - " |
2240 "obsolete()", |
2207 "obsolete()", |
2241 marks[0], marks[0], marks[0]) |
2208 mark, mark, mark) |
2242 revs = set(revs) |
2209 revs = set(revs) |
2243 revs.update(set(rsrevs)) |
2210 revs.update(set(rsrevs)) |
2244 revs = sorted(revs) |
2211 revs = sorted(revs) |
2245 return repomarks, revs |
2212 return marks, revs |
2246 |
2213 |
2247 def _deletebookmark(repo, repomarks, bookmarks): |
2214 def _deletebookmark(repo, marks, mark): |
|
2215 wlock = lock = tr = None |
2248 wlock = lock = tr = None |
2216 try: |
2249 try: |
2217 wlock = repo.wlock() |
2250 wlock = repo.wlock() |
2218 lock = repo.lock() |
2251 lock = repo.lock() |
2219 tr = repo.transaction('prune') |
2252 tr = repo.transaction('prune') |
2220 del marks[mark] |
2253 for bookmark in bookmarks: |
2221 marks.recordchange(tr) |
2254 del repomarks[bookmark] |
|
2255 repomarks.recordchange(tr) |
2222 tr.close() |
2256 tr.close() |
2223 repo.ui.write(_("bookmark '%s' deleted\n") % mark) |
2257 for bookmark in sorted(bookmarks): |
|
2258 repo.ui.write(_("bookmark '%s' deleted\n") % bookmark) |
2224 finally: |
2259 finally: |
2225 lockmod.release(tr, lock, wlock) |
2260 lockmod.release(tr, lock, wlock) |
2226 |
2261 |
2227 |
2262 |
2228 |
2263 |
2241 [('n', 'new', [], _("successor changeset (DEPRECATED)")), |
2276 [('n', 'new', [], _("successor changeset (DEPRECATED)")), |
2242 ('s', 'succ', [], _("successor changeset")), |
2277 ('s', 'succ', [], _("successor changeset")), |
2243 ('r', 'rev', [], _("revisions to prune")), |
2278 ('r', 'rev', [], _("revisions to prune")), |
2244 ('k', 'keep', None, _("does not modify working copy during prune")), |
2279 ('k', 'keep', None, _("does not modify working copy during prune")), |
2245 ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), |
2280 ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")), |
2246 ('', 'fold', False, _("record a fold (multiple precursors, one successors)")), |
2281 ('', 'fold', False, |
2247 ('', 'split', False, _("record a split (on precursor, multiple successors)")), |
2282 _("record a fold (multiple precursors, one successors)")), |
2248 ('B', 'bookmark', '', _("remove revs only reachable from given" |
2283 ('', 'split', False, |
|
2284 _("record a split (on precursor, multiple successors)")), |
|
2285 ('B', 'bookmark', [], _("remove revs only reachable from given" |
2249 " bookmark"))] + metadataopts, |
2286 " bookmark"))] + metadataopts, |
2250 _('[OPTION] [-r] REV...')) |
2287 _('[OPTION] [-r] REV...')) |
2251 # -U --noupdate option to prevent wc update and or bookmarks update ? |
2288 # -U --noupdate option to prevent wc update and or bookmarks update ? |
2252 def cmdprune(ui, repo, *revs, **opts): |
2289 def cmdprune(ui, repo, *revs, **opts): |
2253 """hide changesets by marking them obsolete |
2290 """hide changesets by marking them obsolete |
2273 must acknowledge it by passing ``--split``. Similarly, when you prune multiple |
2310 must acknowledge it by passing ``--split``. Similarly, when you prune multiple |
2274 changesets with a single successor, you must pass the ``--fold`` option. |
2311 changesets with a single successor, you must pass the ``--fold`` option. |
2275 """ |
2312 """ |
2276 revs = scmutil.revrange(repo, list(revs) + opts.get('rev')) |
2313 revs = scmutil.revrange(repo, list(revs) + opts.get('rev')) |
2277 succs = opts['new'] + opts['succ'] |
2314 succs = opts['new'] + opts['succ'] |
2278 bookmark = opts.get('bookmark') |
2315 bookmarks = set(opts.get('bookmark')) |
2279 metadata = _getmetadata(**opts) |
2316 metadata = _getmetadata(**opts) |
2280 biject = opts.get('biject') |
2317 biject = opts.get('biject') |
2281 fold = opts.get('fold') |
2318 fold = opts.get('fold') |
2282 split = opts.get('split') |
2319 split = opts.get('split') |
2283 |
2320 |
2284 options = [o for o in ('biject', 'fold', 'split') if opts.get(o)] |
2321 options = [o for o in ('biject', 'fold', 'split') if opts.get(o)] |
2285 if 1 < len(options): |
2322 if 1 < len(options): |
2286 raise util.Abort(_("can only specify one of %s") % ', '.join(options)) |
2323 raise error.Abort(_("can only specify one of %s") % ', '.join(options)) |
2287 |
2324 |
2288 if bookmark: |
2325 if bookmarks: |
2289 marks,revs = _reachablefrombookmark(repo, revs, bookmark) |
2326 repomarks, revs = _reachablefrombookmark(repo, revs, bookmarks) |
2290 if not revs: |
2327 if not revs: |
2291 # no revisions to prune - delete bookmark immediately |
2328 # no revisions to prune - delete bookmark immediately |
2292 _deletebookmark(repo, marks, bookmark) |
2329 _deletebookmark(repo, repomarks, bookmarks) |
2293 |
2330 |
2294 if not revs: |
2331 if not revs: |
2295 raise util.Abort(_('nothing to prune')) |
2332 raise error.Abort(_('nothing to prune')) |
2296 |
2333 |
2297 wlock = lock = tr = None |
2334 wlock = lock = tr = None |
2298 try: |
2335 try: |
2299 wlock = repo.wlock() |
2336 wlock = repo.wlock() |
2300 lock = repo.lock() |
2337 lock = repo.lock() |
2304 revs.sort() |
2341 revs.sort() |
2305 for p in revs: |
2342 for p in revs: |
2306 cp = repo[p] |
2343 cp = repo[p] |
2307 if not cp.mutable(): |
2344 if not cp.mutable(): |
2308 # note: createmarkers() would have raised something anyway |
2345 # note: createmarkers() would have raised something anyway |
2309 raise util.Abort('cannot prune immutable changeset: %s' % cp, |
2346 raise error.Abort('cannot prune immutable changeset: %s' % cp, |
2310 hint='see "hg help phases" for details') |
2347 hint='see "hg help phases" for details') |
2311 precs.append(cp) |
2348 precs.append(cp) |
2312 if not precs: |
2349 if not precs: |
2313 raise util.Abort('nothing to prune') |
2350 raise error.Abort('nothing to prune') |
2314 |
2351 |
2315 if not obsolete.isenabled(repo, obsolete.allowunstableopt): |
2352 if not obsolete.isenabled(repo, obsolete.allowunstableopt): |
2316 if repo.revs("(%ld::) - %ld", revs, revs): |
2353 if repo.revs("(%ld::) - %ld", revs, revs): |
2317 raise util.Abort(_("cannot prune in the middle of a stack")) |
2354 raise error.Abort(_("cannot prune in the middle of a stack")) |
2318 |
2355 |
2319 # defines successors changesets |
2356 # defines successors changesets |
2320 sucs = scmutil.revrange(repo, succs) |
2357 sucs = scmutil.revrange(repo, succs) |
2321 sucs.sort() |
2358 sucs.sort() |
2322 sucs = tuple(repo[n] for n in sucs) |
2359 sucs = tuple(repo[n] for n in sucs) |
2323 if not biject and len(sucs) > 1 and len(precs) > 1: |
2360 if not biject and len(sucs) > 1 and len(precs) > 1: |
2324 msg = "Can't use multiple successors for multiple precursors" |
2361 msg = "Can't use multiple successors for multiple precursors" |
2325 raise util.Abort(msg) |
2362 raise error.Abort(msg) |
2326 elif biject and len(sucs) != len(precs): |
2363 elif biject and len(sucs) != len(precs): |
2327 msg = "Can't use %d successors for %d precursors" \ |
2364 msg = "Can't use %d successors for %d precursors" \ |
2328 % (len(sucs), len(precs)) |
2365 % (len(sucs), len(precs)) |
2329 raise util.Abort(msg) |
2366 raise error.Abort(msg) |
2330 elif (len(precs) == 1 and len(sucs) > 1) and not split: |
2367 elif (len(precs) == 1 and len(sucs) > 1) and not split: |
2331 msg = "please add --split if you want to do a split" |
2368 msg = "please add --split if you want to do a split" |
2332 raise util.Abort(msg) |
2369 raise error.Abort(msg) |
2333 elif len(sucs) == 1 and len(precs) > 1 and not fold: |
2370 elif len(sucs) == 1 and len(precs) > 1 and not fold: |
2334 msg = "please add --fold if you want to do a fold" |
2371 msg = "please add --fold if you want to do a fold" |
2335 raise util.Abort(msg) |
2372 raise error.Abort(msg) |
2336 elif biject: |
2373 elif biject: |
2337 relations = [(p, (s,)) for p, s in zip(precs, sucs)] |
2374 relations = [(p, (s,)) for p, s in zip(precs, sucs)] |
2338 else: |
2375 else: |
2339 relations = [(p, sucs) for p in precs] |
2376 relations = [(p, sucs) for p in precs] |
2340 |
2377 |
2360 # only reset the dirstate for files that would actually change |
2397 # only reset the dirstate for files that would actually change |
2361 # between the working context and uctx |
2398 # between the working context and uctx |
2362 descendantrevs = repo.revs("%d::." % newnode.rev()) |
2399 descendantrevs = repo.revs("%d::." % newnode.rev()) |
2363 changedfiles = [] |
2400 changedfiles = [] |
2364 for rev in descendantrevs: |
2401 for rev in descendantrevs: |
2365 # blindly reset the files, regardless of what actually changed |
2402 # blindly reset the files, regardless of what actually |
|
2403 # changed |
2366 changedfiles.extend(repo[rev].files()) |
2404 changedfiles.extend(repo[rev].files()) |
2367 |
2405 |
2368 # reset files that only changed in the dirstate too |
2406 # reset files that only changed in the dirstate too |
2369 dirstate = repo.dirstate |
2407 dirstate = repo.dirstate |
2370 dirchanges = [f for f in dirstate if dirstate[f] != 'n'] |
2408 dirchanges = [f for f in dirstate if dirstate[f] != 'n'] |
2371 changedfiles.extend(dirchanges) |
2409 changedfiles.extend(dirchanges) |
2372 repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles) |
2410 repo.dirstate.rebuild(newnode.node(), newnode.manifest(), |
|
2411 changedfiles) |
2373 writedirstate(dirstate, tr) |
2412 writedirstate(dirstate, tr) |
2374 else: |
2413 else: |
2375 bookactive = bmactive(repo) |
2414 bookactive = bmactive(repo) |
2376 # Active bookmark that we don't want to delete (with -B option) |
2415 # Active bookmark that we don't want to delete (with -B option) |
2377 # we deactivate and move it before the update and reactivate it |
2416 # we deactivate and move it before the update and reactivate it |
2378 # after |
2417 # after |
2379 movebookmark = bookactive and not bookmark |
2418 movebookmark = bookactive and not bookmarks |
2380 if movebookmark: |
2419 if movebookmark: |
2381 bmdeactivate(repo) |
2420 bmdeactivate(repo) |
2382 repo._bookmarks[bookactive] = newnode.node() |
2421 repo._bookmarks[bookactive] = newnode.node() |
2383 repo._bookmarks.write() |
2422 repo._bookmarks.recordchange(tr) |
2384 commands.update(ui, repo, newnode.rev()) |
2423 commands.update(ui, repo, newnode.rev()) |
2385 ui.status(_('working directory now at %s\n') % newnode) |
2424 ui.status(_('working directory now at %s\n') % newnode) |
2386 if movebookmark: |
2425 if movebookmark: |
2387 bmactivate(repo, bookactive) |
2426 bmactivate(repo, bookactive) |
2388 |
2427 |
2389 # update bookmarks |
2428 # update bookmarks |
2390 if bookmark: |
2429 if bookmarks: |
2391 _deletebookmark(repo, marks, bookmark) |
2430 _deletebookmark(repo, repomarks, bookmarks) |
2392 |
2431 |
2393 # create markers |
2432 # create markers |
2394 obsolete.createmarkers(repo, relations, metadata=metadata) |
2433 obsolete.createmarkers(repo, relations, metadata=metadata) |
2395 |
2434 |
2396 # informs that changeset have been pruned |
2435 # informs that changeset have been pruned |
2581 try: |
2620 try: |
2582 wlock = repo.wlock() |
2621 wlock = repo.wlock() |
2583 lock = repo.lock() |
2622 lock = repo.lock() |
2584 wctx = repo[None] |
2623 wctx = repo[None] |
2585 if len(wctx.parents()) <= 0: |
2624 if len(wctx.parents()) <= 0: |
2586 raise util.Abort(_("cannot uncommit null changeset")) |
2625 raise error.Abort(_("cannot uncommit null changeset")) |
2587 if len(wctx.parents()) > 1: |
2626 if len(wctx.parents()) > 1: |
2588 raise util.Abort(_("cannot uncommit while merging")) |
2627 raise error.Abort(_("cannot uncommit while merging")) |
2589 old = repo['.'] |
2628 old = repo['.'] |
2590 if old.phase() == phases.public: |
2629 if old.phase() == phases.public: |
2591 raise util.Abort(_("cannot rewrite immutable changeset")) |
2630 raise error.Abort(_("cannot rewrite immutable changeset")) |
2592 if len(old.parents()) > 1: |
2631 if len(old.parents()) > 1: |
2593 raise util.Abort(_("cannot uncommit merge changeset")) |
2632 raise error.Abort(_("cannot uncommit merge changeset")) |
2594 oldphase = old.phase() |
2633 oldphase = old.phase() |
2595 |
2634 |
2596 |
2635 |
2597 rev = None |
2636 rev = None |
2598 if opts.get('rev'): |
2637 if opts.get('rev'): |
2599 rev = scmutil.revsingle(repo, opts.get('rev')) |
2638 rev = scmutil.revsingle(repo, opts.get('rev')) |
2600 ctx = repo[None] |
2639 ctx = repo[None] |
2601 if ctx.p1() == rev or ctx.p2() == rev: |
2640 if ctx.p1() == rev or ctx.p2() == rev: |
2602 raise util.Abort(_("cannot uncommit to parent changeset")) |
2641 raise error.Abort(_("cannot uncommit to parent changeset")) |
2603 |
2642 |
2604 onahead = old.rev() in repo.changelog.headrevs() |
2643 onahead = old.rev() in repo.changelog.headrevs() |
2605 disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) |
2644 disallowunstable = not obsolete.isenabled(repo, |
|
2645 obsolete.allowunstableopt) |
2606 if disallowunstable and not onahead: |
2646 if disallowunstable and not onahead: |
2607 raise util.Abort(_("cannot uncommit in the middle of a stack")) |
2647 raise error.Abort(_("cannot uncommit in the middle of a stack")) |
2608 |
2648 |
2609 # Recommit the filtered changeset |
2649 # Recommit the filtered changeset |
2610 tr = repo.transaction('uncommit') |
2650 tr = repo.transaction('uncommit') |
2611 updatebookmarks = _bookmarksupdater(repo, old.node(), tr) |
2651 updatebookmarks = _bookmarksupdater(repo, old.node(), tr) |
2612 newid = None |
2652 newid = None |
2613 includeorexclude = opts.get('include') or opts.get('exclude') |
2653 includeorexclude = opts.get('include') or opts.get('exclude') |
2614 if (pats or includeorexclude or opts.get('all')): |
2654 if (pats or includeorexclude or opts.get('all')): |
2615 match = scmutil.match(old, pats, opts) |
2655 match = scmutil.match(old, pats, opts) |
2616 newid = _commitfiltered(repo, old, match, target=rev) |
2656 newid = _commitfiltered(repo, old, match, target=rev) |
2617 if newid is None: |
2657 if newid is None: |
2618 raise util.Abort(_('nothing to uncommit'), |
2658 raise error.Abort(_('nothing to uncommit'), |
2619 hint=_("use --all to uncommit all files")) |
2659 hint=_("use --all to uncommit all files")) |
2620 # Move local changes on filtered changeset |
2660 # Move local changes on filtered changeset |
2621 obsolete.createmarkers(repo, [(old, (repo[newid],))]) |
2661 obsolete.createmarkers(repo, [(old, (repo[newid],))]) |
2622 phases.retractboundary(repo, tr, oldphase, [newid]) |
2662 phases.retractboundary(repo, tr, oldphase, [newid]) |
2623 repo.dirstate.beginparentchange() |
2663 repo.dirstate.beginparentchange() |
2654 if markers: |
2695 if markers: |
2655 obsolete.createmarkers(repo, markers) |
2696 obsolete.createmarkers(repo, markers) |
2656 for book in oldbookmarks: |
2697 for book in oldbookmarks: |
2657 repo._bookmarks[book] = new.node() |
2698 repo._bookmarks[book] = new.node() |
2658 if oldbookmarks: |
2699 if oldbookmarks: |
2659 repo._bookmarks.write() |
2700 if not wlock: |
|
2701 wlock = repo.wlock() |
|
2702 if not lock: |
|
2703 lock = repo.lock() |
|
2704 tr = repo.transaction('commit') |
|
2705 repo._bookmarks.recordchange(tr) |
|
2706 tr.close() |
2660 return result |
2707 return result |
2661 finally: |
2708 finally: |
2662 lockmod.release(lock, wlock) |
2709 lockmod.release(tr, lock, wlock) |
2663 |
2710 |
2664 @command('^split', |
2711 @command('^split', |
2665 [('r', 'rev', [], _("revision to fold")), |
2712 [('r', 'rev', [], _("revision to fold")), |
2666 ] + commitopts + commitopts2, |
2713 ] + commitopts + commitopts2, |
2667 _('hg split [OPTION]... [-r] REV')) |
2714 _('hg split [OPTION]... [-r] REV')) |
2668 def cmdsplit(ui, repo, *revs, **opts): |
2715 def cmdsplit(ui, repo, *revs, **opts): |
2669 """split a changeset into smaller changesets (EXPERIMENTAL) |
2716 """split a changeset into smaller changesets |
2670 |
2717 |
2671 By default, split the current revision by prompting for all its hunks to be |
2718 By default, split the current revision by prompting for all its hunks to be |
2672 redistributed into new changesets. |
2719 redistributed into new changesets. |
2673 |
2720 |
2674 Use --rev to split a given changeset instead. |
2721 Use --rev to split a given changeset instead. |
2675 """ |
2722 """ |
2676 tr = wlock = lock = None |
2723 tr = wlock = lock = None |
2677 newcommits = [] |
2724 newcommits = [] |
2678 |
2725 |
2679 revopt = opts.get('rev') |
2726 revarg = (list(revs) + opts.get('rev')) or ['.'] |
2680 if revopt: |
2727 if len(revarg) != 1: |
2681 revs = scmutil.revrange(repo, revopt) |
2728 msg = _("more than one revset is given") |
2682 if len(revs) != 1: |
2729 hnt = _("use either `hg split <rs>` or `hg split --rev <rs>`, not both") |
2683 raise util.Abort(_("you can only specify one revision to split")) |
2730 raise error.Abort(msg, hint=hnt) |
2684 else: |
2731 |
2685 rev = list(revs)[0] |
2732 rev = scmutil.revsingle(repo, revarg[0]) |
2686 else: |
|
2687 rev = '.' |
|
2688 |
|
2689 try: |
2733 try: |
2690 wlock = repo.wlock() |
2734 wlock = repo.wlock() |
2691 lock = repo.lock() |
2735 lock = repo.lock() |
2692 cmdutil.bailifchanged(repo) |
2736 cmdutil.bailifchanged(repo) |
2693 tr = repo.transaction('split') |
2737 tr = repo.transaction('split') |
2765 return cmdprune(ui, repo, *revs, **kwargs) |
2813 return cmdprune(ui, repo, *revs, **kwargs) |
2766 |
2814 |
2767 @command('^touch', |
2815 @command('^touch', |
2768 [('r', 'rev', [], 'revision to update'), |
2816 [('r', 'rev', [], 'revision to update'), |
2769 ('D', 'duplicate', False, |
2817 ('D', 'duplicate', False, |
2770 'do not mark the new revision as successor of the old one')], |
2818 'do not mark the new revision as successor of the old one'), |
|
2819 ('A', 'allowdivergence', False, |
|
2820 'mark the new revision as successor of the old one potentially creating ' |
|
2821 'divergence')], |
2771 # allow to choose the seed ? |
2822 # allow to choose the seed ? |
2772 _('[-r] revs')) |
2823 _('[-r] revs')) |
2773 def touch(ui, repo, *revs, **opts): |
2824 def touch(ui, repo, *revs, **opts): |
2774 """create successors that are identical to their predecessors except for the changeset ID |
2825 """create successors that are identical to their predecessors except |
|
2826 for the changeset ID |
2775 |
2827 |
2776 This is used to "resurrect" changesets |
2828 This is used to "resurrect" changesets |
2777 """ |
2829 """ |
2778 duplicate = opts['duplicate'] |
2830 duplicate = opts['duplicate'] |
|
2831 allowdivergence = opts['allowdivergence'] |
2779 revs = list(revs) |
2832 revs = list(revs) |
2780 revs.extend(opts['rev']) |
2833 revs.extend(opts['rev']) |
2781 if not revs: |
2834 if not revs: |
2782 revs = ['.'] |
2835 revs = ['.'] |
2783 revs = scmutil.revrange(repo, revs) |
2836 revs = scmutil.revrange(repo, revs) |
2784 if not revs: |
2837 if not revs: |
2785 ui.write_err('no revision to touch\n') |
2838 ui.write_err('no revision to touch\n') |
2786 return 1 |
2839 return 1 |
2787 if not duplicate and repo.revs('public() and %ld', revs): |
2840 if not duplicate and repo.revs('public() and %ld', revs): |
2788 raise util.Abort("can't touch public revision") |
2841 raise error.Abort("can't touch public revision") |
|
2842 displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate}) |
2789 wlock = lock = tr = None |
2843 wlock = lock = tr = None |
2790 try: |
2844 try: |
2791 wlock = repo.wlock() |
2845 wlock = repo.wlock() |
2792 lock = repo.lock() |
2846 lock = repo.lock() |
2793 tr = repo.transaction('touch') |
2847 tr = repo.transaction('touch') |
2800 # search for touched parent |
2854 # search for touched parent |
2801 p1 = ctx.p1().node() |
2855 p1 = ctx.p1().node() |
2802 p2 = ctx.p2().node() |
2856 p2 = ctx.p2().node() |
2803 p1 = newmapping.get(p1, p1) |
2857 p1 = newmapping.get(p1, p1) |
2804 p2 = newmapping.get(p2, p2) |
2858 p2 = newmapping.get(p2, p2) |
|
2859 |
|
2860 if not (duplicate or allowdivergence): |
|
2861 # The user hasn't yet decided what to do with the revived |
|
2862 # cset, let's ask |
|
2863 sset = obsolete.successorssets(repo, ctx.node()) |
|
2864 nodivergencerisk = len(sset) == 0 or ( |
|
2865 len(sset) == 1 and |
|
2866 len(sset[0]) == 1 and |
|
2867 repo[sset[0][0]].rev() == ctx.rev() |
|
2868 ) |
|
2869 if nodivergencerisk: |
|
2870 duplicate = False |
|
2871 else: |
|
2872 displayer.show(ctx) |
|
2873 index = ui.promptchoice( |
|
2874 _("reviving this changeset will create divergence" |
|
2875 " unless you make a duplicate.\n(a)llow divergence or" |
|
2876 " (d)uplicate the changeset? $$ &Allowdivergence $$ " |
|
2877 "&Duplicate"), 0) |
|
2878 choice = ['allowdivergence', 'duplicate'][index] |
|
2879 if choice == 'allowdivergence': |
|
2880 duplicate = False |
|
2881 else: |
|
2882 duplicate = True |
|
2883 |
2805 new, unusedvariable = rewrite(repo, ctx, [], ctx, |
2884 new, unusedvariable = rewrite(repo, ctx, [], ctx, |
2806 [p1, p2], |
2885 [p1, p2], |
2807 commitopts={'extra': extra}) |
2886 commitopts={'extra': extra}) |
2808 # store touched version to help potential children |
2887 # store touched version to help potential children |
2809 newmapping[ctx.node()] = new |
2888 newmapping[ctx.node()] = new |
|
2889 |
2810 if not duplicate: |
2890 if not duplicate: |
2811 obsolete.createmarkers(repo, [(ctx, (repo[new],))]) |
2891 obsolete.createmarkers(repo, [(ctx, (repo[new],))]) |
2812 phases.retractboundary(repo, tr, ctx.phase(), [new]) |
2892 phases.retractboundary(repo, tr, ctx.phase(), [new]) |
2813 if ctx in repo[None].parents(): |
2893 if ctx in repo[None].parents(): |
2814 repo.dirstate.beginparentchange() |
2894 repo.dirstate.beginparentchange() |
2861 hg fold foo::@ --exact |
2941 hg fold foo::@ --exact |
2862 """ |
2942 """ |
2863 revs = list(revs) |
2943 revs = list(revs) |
2864 revs.extend(opts['rev']) |
2944 revs.extend(opts['rev']) |
2865 if not revs: |
2945 if not revs: |
2866 raise util.Abort(_('no revisions specified')) |
2946 raise error.Abort(_('no revisions specified')) |
2867 |
2947 |
2868 revs = scmutil.revrange(repo, revs) |
2948 revs = scmutil.revrange(repo, revs) |
2869 |
2949 |
2870 if not opts['exact']: |
2950 if not opts['exact']: |
2871 # Try to extend given revision starting from the working directory |
2951 # Try to extend given revision starting from the working directory |
2872 extrevs = repo.revs('(%ld::.) or (.::%ld)', revs, revs) |
2952 extrevs = repo.revs('(%ld::.) or (.::%ld)', revs, revs) |
2873 discardedrevs = [r for r in revs if r not in extrevs] |
2953 discardedrevs = [r for r in revs if r not in extrevs] |
2874 if discardedrevs: |
2954 if discardedrevs: |
2875 raise util.Abort(_("cannot fold non-linear revisions"), |
2955 raise error.Abort(_("cannot fold non-linear revisions"), |
2876 hint=_("given revisions are unrelated to parent " |
2956 hint=_("given revisions are unrelated to parent " |
2877 "of working directory")) |
2957 "of working directory")) |
2878 revs = extrevs |
2958 revs = extrevs |
2879 |
2959 |
2880 if len(revs) == 1: |
2960 if len(revs) == 1: |
2881 ui.write_err(_('single revision specified, nothing to fold\n')) |
2961 ui.write_err(_('single revision specified, nothing to fold\n')) |
2882 return 1 |
2962 return 1 |
2883 |
2963 |
2884 roots = repo.revs('roots(%ld)', revs) |
2964 roots = repo.revs('roots(%ld)', revs) |
2885 if len(roots) > 1: |
2965 if len(roots) > 1: |
2886 raise util.Abort(_("cannot fold non-linear revisions " |
2966 raise error.Abort(_("cannot fold non-linear revisions " |
2887 "(multiple roots given)")) |
2967 "(multiple roots given)")) |
2888 root = repo[roots.first()] |
2968 root = repo[roots.first()] |
2889 if root.phase() <= phases.public: |
2969 if root.phase() <= phases.public: |
2890 raise util.Abort(_("cannot fold public revisions")) |
2970 raise error.Abort(_("cannot fold public revisions")) |
2891 heads = repo.revs('heads(%ld)', revs) |
2971 heads = repo.revs('heads(%ld)', revs) |
2892 if len(heads) > 1: |
2972 if len(heads) > 1: |
2893 raise util.Abort(_("cannot fold non-linear revisions " |
2973 raise error.Abort(_("cannot fold non-linear revisions " |
2894 "(multiple heads given)")) |
2974 "(multiple heads given)")) |
2895 head = repo[heads.first()] |
2975 head = repo[heads.first()] |
2896 disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) |
2976 disallowunstable = not obsolete.isenabled(repo, obsolete.allowunstableopt) |
2897 if disallowunstable: |
2977 if disallowunstable: |
2898 if repo.revs("(%ld::) - %ld", revs, revs): |
2978 if repo.revs("(%ld::) - %ld", revs, revs): |
2899 raise util.Abort(_("cannot fold chain not ending with a head "\ |
2979 raise error.Abort(_("cannot fold chain not ending with a head "\ |
2900 "or with branching")) |
2980 "or with branching")) |
2901 wlock = lock = None |
2981 wlock = lock = None |
2902 try: |
2982 try: |
2903 wlock = repo.wlock() |
2983 wlock = repo.wlock() |
2904 lock = repo.lock() |
2984 lock = repo.lock() |
3509 sha.update(m) |
3595 sha.update(m) |
3510 if entry: |
3596 if entry: |
3511 cache.append((ctx.node(), sha.digest())) |
3597 cache.append((ctx.node(), sha.digest())) |
3512 else: |
3598 else: |
3513 cache.append((ctx.node(), nullid)) |
3599 cache.append((ctx.node(), nullid)) |
3514 repo.ui.progress("preparing locally", i, total=len(unfi)) |
3600 repo.ui.progress(_("preparing locally"), i, total=len(unfi)) |
3515 repo.ui.progress("preparing locally", None) |
3601 repo.ui.progress(_("preparing locally"), None) |
3516 return cache |
3602 return cache |
3517 |
3603 |
3518 @command('debugobsrelsethashtree', |
3604 @command('debugobsrelsethashtree', |
3519 [('', 'v0', None, 'hash on marker format "0"'), |
3605 [('', 'v0', None, 'hash on marker format "0"'), |
3520 ('', 'v1', None, 'hash on marker format "1" (default)') |
3606 ('', 'v1', None, 'hash on marker format "1" (default)')] , _('')) |
3521 ,] , _('')) |
|
3522 def debugobsrelsethashtree(ui, repo, v0=False, v1=False): |
3607 def debugobsrelsethashtree(ui, repo, v0=False, v1=False): |
3523 """display Obsolete markers, Relevant Set, Hash Tree |
3608 """display Obsolete markers, Relevant Set, Hash Tree |
3524 changeset-node obsrelsethashtree-node |
3609 changeset-node obsrelsethashtree-node |
3525 |
3610 |
3526 It computed form the "orsht" of its parent and markers |
3611 It computed form the "orsht" of its parent and markers |
3527 relevant to the changeset itself.""" |
3612 relevant to the changeset itself.""" |
3528 if v0 and v1: |
3613 if v0 and v1: |
3529 raise util.Abort('cannot only specify one format') |
3614 raise error.Abort('cannot only specify one format') |
3530 elif v0: |
3615 elif v0: |
3531 treefunc = _obsrelsethashtreefm0 |
3616 treefunc = _obsrelsethashtreefm0 |
3532 else: |
3617 else: |
3533 treefunc = _obsrelsethashtreefm1 |
3618 treefunc = _obsrelsethashtreefm1 |
3534 |
3619 |
3619 break |
3704 break |
3620 else: |
3705 else: |
3621 help.helptable.append((["evolution"], _("Safely Rewriting History"), |
3706 help.helptable.append((["evolution"], _("Safely Rewriting History"), |
3622 _helploader)) |
3707 _helploader)) |
3623 help.helptable.sort() |
3708 help.helptable.sort() |
|
3709 |
|
3710 def _relocatecommit(repo, orig, commitmsg): |
|
3711 if commitmsg is None: |
|
3712 commitmsg = orig.description() |
|
3713 extra = dict(orig.extra()) |
|
3714 if 'branch' in extra: |
|
3715 del extra['branch'] |
|
3716 extra['rebase_source'] = orig.hex() |
|
3717 |
|
3718 backup = repo.ui.backupconfig('phases', 'new-commit') |
|
3719 try: |
|
3720 targetphase = max(orig.phase(), phases.draft) |
|
3721 repo.ui.setconfig('phases', 'new-commit', targetphase, 'rebase') |
|
3722 # Commit might fail if unresolved files exist |
|
3723 nodenew = repo.commit(text=commitmsg, user=orig.user(), |
|
3724 date=orig.date(), extra=extra) |
|
3725 finally: |
|
3726 repo.ui.restoreconfig(backup) |
|
3727 return nodenew |
|
3728 |
|
3729 def _finalizerelocate(repo, orig, dest, nodenew, tr): |
|
3730 destbookmarks = repo.nodebookmarks(dest.node()) |
|
3731 nodesrc = orig.node() |
|
3732 destphase = repo[nodesrc].phase() |
|
3733 oldbookmarks = repo.nodebookmarks(nodesrc) |
|
3734 if nodenew is not None: |
|
3735 phases.retractboundary(repo, tr, destphase, [nodenew]) |
|
3736 obsolete.createmarkers(repo, [(repo[nodesrc], (repo[nodenew],))]) |
|
3737 for book in oldbookmarks: |
|
3738 repo._bookmarks[book] = nodenew |
|
3739 else: |
|
3740 obsolete.createmarkers(repo, [(repo[nodesrc], ())]) |
|
3741 # Behave like rebase, move bookmarks to dest |
|
3742 for book in oldbookmarks: |
|
3743 repo._bookmarks[book] = dest.node() |
|
3744 for book in destbookmarks: # restore bookmark that rebase move |
|
3745 repo._bookmarks[book] = dest.node() |
|
3746 if oldbookmarks or destbookmarks: |
|
3747 repo._bookmarks.recordchange(tr) |
|
3748 |
|
3749 evolvestateversion = 0 |
|
3750 |
|
3751 @eh.uisetup |
|
3752 def setupevolveunfinished(ui): |
|
3753 data = ('evolvestate', True, False, _('evolve in progress'), |
|
3754 _("use 'hg evolve --continue' or 'hg update' to abort")) |
|
3755 cmdutil.unfinishedstates.append(data) |
|
3756 |
|
3757 @eh.wrapfunction(hg, 'clean') |
|
3758 def clean(orig, repo, *args, **kwargs): |
|
3759 ret = orig(repo, *args, **kwargs) |
|
3760 util.unlinkpath(repo.join('evolvestate'), ignoremissing=True) |
|
3761 return ret |
|
3762 |
|
3763 def _evolvestatewrite(repo, state): |
|
3764 # [version] |
|
3765 # [type][length][content] |
|
3766 # |
|
3767 # `version` is a 4 bytes integer (handled at higher level) |
|
3768 # `type` is a single character, `length` is a 4 byte integer, and |
|
3769 # `content` is an arbitrary byte sequence of length `length`. |
|
3770 f = repo.vfs('evolvestate', 'w') |
|
3771 try: |
|
3772 f.write(_pack('>I', evolvestateversion)) |
|
3773 current = state['current'] |
|
3774 key = 'C' # as in 'current' |
|
3775 format = '>sI%is' % len(current) |
|
3776 f.write(_pack(format, key, len(current), current)) |
|
3777 finally: |
|
3778 f.close() |
|
3779 |
|
3780 def _evolvestateread(repo): |
|
3781 try: |
|
3782 f = repo.vfs('evolvestate') |
|
3783 except IOError, err: |
|
3784 if err.errno != errno.ENOENT: |
|
3785 raise |
|
3786 return None |
|
3787 try: |
|
3788 versionblob = f.read(4) |
|
3789 if len(versionblob) < 4: |
|
3790 repo.ui.debug('ignoring corrupted evolvestte (file contains %i bits)' |
|
3791 % len(versionblob)) |
|
3792 return None |
|
3793 version = _unpack('>I', versionblob)[0] |
|
3794 if version != evolvestateversion: |
|
3795 raise error.Abort(_('unknown evolvestate version %i') |
|
3796 % version, hint=_('upgrade your evolve')) |
|
3797 records = [] |
|
3798 data = f.read() |
|
3799 off = 0 |
|
3800 end = len(data) |
|
3801 while off < end: |
|
3802 rtype = data[off] |
|
3803 off += 1 |
|
3804 length = _unpack('>I', data[off:(off + 4)])[0] |
|
3805 off += 4 |
|
3806 record = data[off:(off + length)] |
|
3807 off += length |
|
3808 if rtype == 't': |
|
3809 rtype, record = record[0], record[1:] |
|
3810 records.append((rtype, record)) |
|
3811 state = {} |
|
3812 for rtype, rdata in records: |
|
3813 if rtype == 'C': |
|
3814 state['current'] = rdata |
|
3815 elif rtype.lower(): |
|
3816 repo.ui.debug('ignore evolve state record type %s' % rtype) |
|
3817 else: |
|
3818 raise error.Abort(_('unknown evolvestate field type %r') |
|
3819 % rtype, hint=_('upgrade your evolve')) |
|
3820 return state |
|
3821 finally: |
|
3822 f.close() |
|
3823 |
|
3824 def _evolvestatedelete(repo): |
|
3825 util.unlinkpath(repo.join('evolvestate'), ignoremissing=True) |
|
3826 |
|
3827 def _evolvemerge(repo, orig, dest, pctx, keepbranch): |
|
3828 """Used by the evolve function to merge dest on top of pctx. |
|
3829 return the same tuple as merge.graft""" |
|
3830 if repo['.'].rev() != dest.rev(): |
|
3831 merge.update(repo, dest, False, True, False) |
|
3832 if bmactive(repo): |
|
3833 repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo)) |
|
3834 bmdeactivate(repo) |
|
3835 if keepbranch: |
|
3836 repo.dirstate.setbranch(orig.branch()) |
|
3837 |
|
3838 try: |
|
3839 r = merge.graft(repo, orig, pctx, ['local', 'graft'], True) |
|
3840 except TypeError: |
|
3841 # not using recent enough mercurial |
|
3842 if len(orig.parents()) == 2: |
|
3843 raise error.Abort( |
|
3844 _("no support for evolving merge changesets yet"), |
|
3845 hint=_("Redo the merge and use `hg prune <old> --succ " |
|
3846 "<new>` to obsolete the old one")) |
|
3847 |
|
3848 r = merge.graft(repo, orig, pctx, ['local', 'graft']) |
|
3849 return r |