hgext/evolve.py
branchstable
changeset 1602 f932853783a5
parent 1598 2a08ef812b84
child 1610 9e93ddf03704
equal deleted inserted replaced
1584:190e4e526c66 1602:f932853783a5
    65 import struct
    65 import struct
    66 import re
    66 import re
    67 import collections
    67 import collections
    68 import socket
    68 import socket
    69 import errno
    69 import errno
       
    70 import struct
    70 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
    71 sha1re = re.compile(r'\b[0-9a-f]{6,40}\b')
    71 
    72 
    72 import mercurial
    73 import mercurial
    73 from mercurial import util
    74 from mercurial import util
    74 from mercurial import repair
    75 from mercurial import repair
    84     gboptslist = gboptsmap = None
    85     gboptslist = gboptsmap = None
    85 
    86 
    86 # Flags for enabling optional parts of evolve
    87 # Flags for enabling optional parts of evolve
    87 commandopt = 'allnewcommands'
    88 commandopt = 'allnewcommands'
    88 
    89 
    89 from mercurial import bookmarks
    90 from mercurial import bookmarks as bookmarksmod
    90 from mercurial import cmdutil
    91 from mercurial import cmdutil
    91 from mercurial import commands
    92 from mercurial import commands
    92 from mercurial import context
    93 from mercurial import context
    93 from mercurial import copies
    94 from mercurial import copies
    94 from mercurial import error
    95 from mercurial import error
   114 
   115 
   115 cmdtable = {}
   116 cmdtable = {}
   116 command = cmdutil.command(cmdtable)
   117 command = cmdutil.command(cmdtable)
   117 
   118 
   118 _pack = struct.pack
   119 _pack = struct.pack
       
   120 _unpack = struct.unpack
   119 
   121 
   120 if gboptsmap is not None:
   122 if gboptsmap is not None:
   121     memfilectx = context.memfilectx
   123     memfilectx = context.memfilectx
   122 elif gboptslist is not None:
   124 elif gboptslist is not None:
   123     oldmemfilectx = context.memfilectx
   125     oldmemfilectx = context.memfilectx
   124     def memfilectx(repo, *args, **kwargs):
   126     def memfilectx(repo, *args, **kwargs):
   125         return oldmemfilectx(*args, **kwargs)
   127         return oldmemfilectx(*args, **kwargs)
   126 else:
   128 else:
   127     raise ImportError('evolve needs version %s or above' % min(testedwith.split()))
   129     raise ImportError('evolve needs version %s or above' %
       
   130                       min(testedwith.split()))
   128 
   131 
   129 aliases, entry = cmdutil.findcmd('commit', commands.table)
   132 aliases, entry = cmdutil.findcmd('commit', commands.table)
   130 hasinteractivemode = any(['interactive' in e for e in entry[1]])
   133 hasinteractivemode = any(['interactive' in e for e in entry[1]])
   131 if hasinteractivemode:
   134 if hasinteractivemode:
   132     interactiveopt = [['i', 'interactive', None, _('use interactive mode')]]
   135     interactiveopt = [['i', 'interactive', None, _('use interactive mode')]]
   413         for cmd in evolvecommands:
   416         for cmd in evolvecommands:
   414             matchingevolvecommands = [e for e in cmdtable.keys() if cmd in e]
   417             matchingevolvecommands = [e for e in cmdtable.keys() if cmd in e]
   415             if not matchingevolvecommands:
   418             if not matchingevolvecommands:
   416                 raise error.Abort(_('unknown command: %s') % cmd)
   419                 raise error.Abort(_('unknown command: %s') % cmd)
   417             elif len(matchingevolvecommands) > 1:
   420             elif len(matchingevolvecommands) > 1:
   418                 raise error.Abort(_('ambiguous command specification: "%s" matches %r')
   421                 msg = _('ambiguous command specification: "%s" matches %r')
   419                                   % (cmd, matchingevolvecommands))
   422                 raise error.Abort(msg % (cmd, matchingevolvecommands))
   420             else:
   423             else:
   421                 whitelist.add(matchingevolvecommands[0])
   424                 whitelist.add(matchingevolvecommands[0])
   422         for disabledcmd in set(cmdtable) - whitelist:
   425         for disabledcmd in set(cmdtable) - whitelist:
   423             del cmdtable[disabledcmd]
   426             del cmdtable[disabledcmd]
   424 
   427 
   745 def push(orig, repo, *args, **opts):
   748 def push(orig, repo, *args, **opts):
   746     """Add a hint for "hg evolve" when troubles make push fails
   749     """Add a hint for "hg evolve" when troubles make push fails
   747     """
   750     """
   748     try:
   751     try:
   749         return orig(repo, *args, **opts)
   752         return orig(repo, *args, **opts)
   750     except util.Abort, ex:
   753     except error.Abort as ex:
   751         hint = _("use 'hg evolve' to get a stable history "
   754         hint = _("use 'hg evolve' to get a stable history "
   752                  "or --force to ignore warnings")
   755                  "or --force to ignore warnings")
   753         if (len(ex.args) >= 1
   756         if (len(ex.args) >= 1
   754             and ex.args[0].startswith('push includes ')
   757             and ex.args[0].startswith('push includes ')
   755             and ex.hint is None):
   758             and ex.hint is None):
   762         if count:
   765         if count:
   763             ui.write(s)
   766             ui.write(s)
   764         else:
   767         else:
   765             ui.note(s)
   768             ui.note(s)
   766 
   769 
   767     nbunstable = len(getrevs(repo, 'unstable'))
   770     # util.versiontuple was introduced in 3.6.2
   768     nbbumped = len(getrevs(repo, 'bumped'))
   771     if not util.safehasattr(util, 'versiontuple'):
   769     nbdivergent = len(getrevs(repo, 'divergent'))
   772         nbunstable = len(getrevs(repo, 'unstable'))
   770     write('unstable: %i changesets\n', nbunstable)
   773         nbbumped = len(getrevs(repo, 'bumped'))
   771     write('bumped: %i changesets\n', nbbumped)
   774         nbdivergent = len(getrevs(repo, 'divergent'))
   772     write('divergent: %i changesets\n', nbdivergent)
   775         write('unstable: %i changesets\n', nbunstable)
       
   776         write('bumped: %i changesets\n', nbbumped)
       
   777         write('divergent: %i changesets\n', nbdivergent)
       
   778     else:
       
   779         # In 3.6.2, summary in core gained this feature, no need to display it
       
   780         pass
   773 
   781 
   774 @eh.extsetup
   782 @eh.extsetup
   775 def obssummarysetup(ui):
   783 def obssummarysetup(ui):
   776     cmdutil.summaryhooks.add('evolve', summaryhook)
   784     cmdutil.summaryhooks.add('evolve', summaryhook)
   777 
   785 
   787     try:
   795     try:
   788         rebase = extensions.find('rebase')
   796         rebase = extensions.find('rebase')
   789         if rebase:
   797         if rebase:
   790             extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors)
   798             extensions.wrapcommand(rebase.cmdtable, 'rebase', warnobserrors)
   791     except KeyError:
   799     except KeyError:
   792         pass  # rebase not found
   800         pass # rebase not found
   793     try:
   801     try:
   794         histedit = extensions.find('histedit')
   802         histedit = extensions.find('histedit')
   795         if histedit:
   803         if histedit:
   796             extensions.wrapcommand(histedit.cmdtable, 'histedit', warnobserrors)
   804             extensions.wrapcommand(histedit.cmdtable, 'histedit', warnobserrors)
   797     except KeyError:
   805     except KeyError:
   798         pass  # histedit not found
   806         pass # histedit not found
   799 
   807 
   800 #####################################################################
   808 #####################################################################
   801 ### Old Evolve extension content                                  ###
   809 ### Old Evolve extension content                                  ###
   802 #####################################################################
   810 #####################################################################
   803 
   811 
   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.
  1014     return updatebookmarks
   991     return updatebookmarks
  1015 
   992 
  1016 ### bookmarks api compatibility layer ###
   993 ### bookmarks api compatibility layer ###
  1017 def bmdeactivate(repo):
   994 def bmdeactivate(repo):
  1018     try:
   995     try:
  1019         return bookmarks.deactivate(repo)
   996         return bookmarksmod.deactivate(repo)
  1020     except AttributeError:
   997     except AttributeError:
  1021         return bookmarks.unsetcurrent(repo)
   998         return bookmarksmod.unsetcurrent(repo)
  1022 def bmactivate(repo, book):
   999 def bmactivate(repo, book):
  1023     try:
  1000     try:
  1024         return bookmarks.activate(repo, book)
  1001         return bookmarksmod.activate(repo, book)
  1025     except AttributeError:
  1002     except AttributeError:
  1026         return bookmarks.setcurrent(repo, book)
  1003         return bookmarksmod.setcurrent(repo, book)
  1027 
  1004 
  1028 def bmactive(repo):
  1005 def bmactive(repo):
  1029     try:
  1006     try:
  1030         return repo._activebookmark
  1007         return repo._activebookmark
  1031     except AttributeError:
  1008     except AttributeError:
  1154                     parents = tuple(p.node() for p in ctx.parents())
  1131                     parents = tuple(p.node() for p in ctx.parents())
  1155                     before = len(store._all)
  1132                     before = len(store._all)
  1156                     store.create(tr, mark[0], mark[1], mark[2], marks[3],
  1133                     store.create(tr, mark[0], mark[1], mark[2], marks[3],
  1157                                  parents=parents)
  1134                                  parents=parents)
  1158                     if len(store._all) - before:
  1135                     if len(store._all) - before:
  1159                         ui.write('created new markers for %i\n' % rev)
  1136                         ui.write(_('created new markers for %i\n') % rev)
  1160             ui.progress(pgop, idx, total=pgtotal)
  1137             ui.progress(pgop, idx, total=pgtotal)
  1161         tr.close()
  1138         tr.close()
  1162         ui.progress(pgop, None)
  1139         ui.progress(pgop, None)
  1163     finally:
  1140     finally:
  1164         lockmod.release(tr, lock, wlock)
  1141         lockmod.release(tr, lock, wlock)
  1184             clustersmap[n] = c
  1161             clustersmap[n] = c
  1185 
  1162 
  1186     store = repo.obsstore
  1163     store = repo.obsstore
  1187     unfi = repo.unfiltered()
  1164     unfi = repo.unfiltered()
  1188     nm = unfi.changelog.nodemap
  1165     nm = unfi.changelog.nodemap
  1189     ui.write('markers total:              %9i\n' % len(store._all))
  1166     ui.write(_('markers total:              %9i\n') % len(store._all))
  1190     sucscount = [0, 0 , 0, 0]
  1167     sucscount = [0, 0 , 0, 0]
  1191     known = 0
  1168     known = 0
  1192     parentsdata = 0
  1169     parentsdata = 0
  1193     metakeys = {}
  1170     metakeys = {}
  1194     # node -> cluster mapping
  1171     # node -> cluster mapping
  1195     #   a cluster is a (set(nodes), set(markers)) tuple
  1172     #   a cluster is a (set(nodes), set(markers)) tuple
  1196     clustersmap = {}
  1173     clustersmap = {}
  1197     # same data using parent information
  1174     # same data using parent information
  1198     pclustersmap= {}
  1175     pclustersmap = {}
  1199     for mark in store:
  1176     for mark in store:
  1200         if mark[0] in nm:
  1177         if mark[0] in nm:
  1201             known += 1
  1178             known += 1
  1202         nbsucs = len(mark[1])
  1179         nbsucs = len(mark[1])
  1203         sucscount[min(nbsucs, 3)] += 1
  1180         sucscount[min(nbsucs, 3)] += 1
  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:
  1365         if othertroubles:
  1345         if othertroubles:
  1366             hint = hintmap['+'.join(othertroubles)]
  1346             hint = hintmap['+'.join(othertroubles)]
  1367         else:
  1347         else:
  1368             l = len(troubled[targetcat])
  1348             l = len(troubled[targetcat])
  1369             if l:
  1349             if l:
  1370                 hint = (_("%d other %s in the repository, do you want --any or --rev")
  1350                 hint = _("%d other %s in the repository, do you want --any "
  1371                         % (l, targetcat))
  1351                         "or --rev") % (l, targetcat)
  1372             else:
  1352             else:
  1373                 othertroubles = []
  1353                 othertroubles = []
  1374                 for cat in unselectedcategories:
  1354                 for cat in unselectedcategories:
  1375                     if troubled[cat]:
  1355                     if troubled[cat]:
  1376                         othertroubles.append(cat)
  1356                         othertroubles.append(cat)
  1387     else:
  1367     else:
  1388         return 1
  1368         return 1
  1389 
  1369 
  1390 def _cleanup(ui, repo, startnode, showprogress):
  1370 def _cleanup(ui, repo, startnode, showprogress):
  1391     if showprogress:
  1371     if showprogress:
  1392         ui.progress('evolve', None)
  1372         ui.progress(_('evolve'), None)
  1393     if repo['.'] != startnode:
  1373     if repo['.'] != startnode:
  1394         ui.status(_('working directory is now at %s\n') % repo['.'])
  1374         ui.status(_('working directory is now at %s\n') % repo['.'])
  1395 
  1375 
  1396 class MultipleSuccessorsError(RuntimeError):
  1376 class MultipleSuccessorsError(RuntimeError):
  1397     """Exception raised by _singlesuccessor when multiple successor sets exists
  1377     """Exception raised by _singlesuccessor when multiple successor sets exists
  1440     for r in revs:
  1420     for r in revs:
  1441         dependencies[r] = set()
  1421         dependencies[r] = set()
  1442         for p in repo[r].parents():
  1422         for p in repo[r].parents():
  1443             try:
  1423             try:
  1444                 succ = _singlesuccessor(repo, p)
  1424                 succ = _singlesuccessor(repo, p)
  1445             except MultipleSuccessorsError, exc:
  1425             except MultipleSuccessorsError as exc:
  1446                 dependencies[r] = exc.successorssets
  1426                 dependencies[r] = exc.successorssets
  1447                 continue
  1427                 continue
  1448             if succ in revs:
  1428             if succ in revs:
  1449                 dependencies[r].add(succ)
  1429                 dependencies[r].add(succ)
  1450                 rdependencies[succ].add(r)
  1430                 rdependencies[succ].add(r)
  1474     if allopt or revopt:
  1454     if allopt or revopt:
  1475         revs = repo.revs(targetcat+'()')
  1455         revs = repo.revs(targetcat+'()')
  1476         if revopt:
  1456         if revopt:
  1477             revs = scmutil.revrange(repo, revopt) & revs
  1457             revs = scmutil.revrange(repo, revopt) & revs
  1478         elif not anyopt and targetcat == 'unstable':
  1458         elif not anyopt and targetcat == 'unstable':
  1479             revs = set(_aspiringdescendant(repo, repo.revs('(.::) - obsolete()::')))
  1459             revs = set(_aspiringdescendant(repo,
       
  1460                                            repo.revs('(.::) - obsolete()::')))
  1480         if targetcat == 'divergent':
  1461         if targetcat == 'divergent':
  1481             # Pick one divergent per group of divergents
  1462             # Pick one divergent per group of divergents
  1482             revs = _dedupedivergents(repo, revs)
  1463             revs = _dedupedivergents(repo, revs)
  1483     elif anyopt:
  1464     elif anyopt:
  1484         revs = repo.revs('first(%s())' % (targetcat))
  1465         revs = repo.revs('first(%s())' % (targetcat))
  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']
  1618     troublecategories = ['bumped', 'divergent', 'unstable']
  1605     troublecategories = ['bumped', 'divergent', 'unstable']
  1619     specifiedcategories = [t for t in troublecategories if opts[t]]
  1606     specifiedcategories = [t for t in troublecategories if opts[t]]
  1620     targetcat = 'unstable'
  1607     targetcat = 'unstable'
  1621     if 1 < len(specifiedcategories):
  1608     if 1 < len(specifiedcategories):
  1622         msg = _('cannot specify more than one trouble category to solve (yet)')
  1609         msg = _('cannot specify more than one trouble category to solve (yet)')
  1623         raise util.Abort(msg)
  1610         raise error.Abort(msg)
  1624     elif len(specifiedcategories) == 1:
  1611     elif len(specifiedcategories) == 1:
  1625         targetcat = specifiedcategories[0]
  1612         targetcat = specifiedcategories[0]
  1626     elif repo['.'].obsolete():
  1613     elif repo['.'].obsolete():
  1627         displayer = cmdutil.show_changeset(ui, repo, {'template': shorttemplate})
  1614         displayer = cmdutil.show_changeset(ui, repo,
       
  1615                                            {'template': shorttemplate})
  1628         # no args and parent is obsolete, update to successors
  1616         # no args and parent is obsolete, update to successors
  1629         try:
  1617         try:
  1630             ctx = repo[_singlesuccessor(repo, repo['.'])]
  1618             ctx = repo[_singlesuccessor(repo, repo['.'])]
  1631         except MultipleSuccessorsError, exc:
  1619         except MultipleSuccessorsError as exc:
  1632             repo.ui.write_err('parent is obsolete with multiple successors:\n')
  1620             repo.ui.write_err('parent is obsolete with multiple successors:\n')
  1633             for ln in exc.successorssets:
  1621             for ln in exc.successorssets:
  1634                 for n in ln:
  1622                 for n in ln:
  1635                     displayer.show(repo[n])
  1623                     displayer.show(repo[n])
  1636             return 2
  1624             return 2
  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:
  1788         repo.ui.write(_('move:'))
  1795         repo.ui.write(_('move:'))
  1789         displayer.show(orig)
  1796         displayer.show(orig)
  1790         repo.ui.write(_('atop:'))
  1797         repo.ui.write(_('atop:'))
  1791         displayer.show(target)
  1798         displayer.show(target)
  1792     if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
  1799     if confirm and ui.prompt('perform evolve? [Ny]', 'n') != 'y':
  1793             raise util.Abort(_('evolve aborted by user'))
  1800             raise error.Abort(_('evolve aborted by user'))
  1794     if progresscb: progresscb()
  1801     if progresscb: progresscb()
  1795     todo = 'hg rebase -r %s -d %s\n' % (orig, target)
  1802     todo = 'hg rebase -r %s -d %s\n' % (orig, target)
  1796     if dryrun:
  1803     if dryrun:
  1797         repo.ui.write(todo)
  1804         repo.ui.write(todo)
  1798     else:
  1805     else:
  1799         repo.ui.note(todo)
  1806         repo.ui.note(todo)
  1800         if progresscb: progresscb()
  1807         if progresscb: progresscb()
  1801         keepbranch = orig.p1().branch() != orig.branch()
  1808         keepbranch = orig.p1().branch() != orig.branch()
  1802         try:
  1809         try:
  1803             relocate(repo, orig, target, keepbranch)
  1810             relocate(repo, orig, target, pctx, keepbranch)
  1804         except MergeFailure:
  1811         except MergeFailure:
  1805             repo.opener.write('graftstate', orig.hex() + '\n')
  1812             _evolvestatewrite(repo, {'current': orig.node()})
  1806             repo.ui.write_err(_('evolve failed!\n'))
  1813             repo.ui.write_err(_('evolve failed!\n'))
  1807             repo.ui.write_err(
  1814             repo.ui.write_err(
  1808                 _('fix conflict and run "hg evolve --continue"'
  1815                 _('fix conflict and run "hg evolve --continue"'
  1809                   ' or use "hg update -C" to abort\n'))
  1816                   ' or use "hg update -C" to abort\n'))
  1810             raise
  1817             raise
  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(
  1909         else:
  1918         else:
  1910             phases.retractboundary(repo, tr, bumped.phase(), [newid])
  1919             phases.retractboundary(repo, tr, bumped.phase(), [newid])
  1911             obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
  1920             obsolete.createmarkers(repo, [(tmpctx, (repo[newid],))],
  1912                                    flag=obsolete.bumpedfix)
  1921                                    flag=obsolete.bumpedfix)
  1913         bmupdate(newid)
  1922         bmupdate(newid)
  1914         tr.close()
       
  1915         repo.ui.status(_('committed as %s\n') % node.short(newid))
  1923         repo.ui.status(_('committed as %s\n') % node.short(newid))
  1916     finally:
  1924     finally:
  1917         tr.release()
  1925         pass # TODO: remove this redundant try/finally block
  1918     # reroute the working copy parent to the new changeset
  1926     # reroute the working copy parent to the new changeset
  1919     repo.dirstate.beginparentchange()
  1927     repo.dirstate.beginparentchange()
  1920     repo.dirstate.setparents(newid, node.nullid)
  1928     repo.dirstate.setparents(newid, node.nullid)
  1921     repo.dirstate.endparentchange()
  1929     repo.dirstate.endparentchange()
  1922 
  1930 
  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='')
  2021             # no changes
  2045             # no changes
  2022         else:
  2046         else:
  2023             new = repo['.']
  2047             new = repo['.']
  2024         obsolete.createmarkers(repo, [(other, (new,))])
  2048         obsolete.createmarkers(repo, [(other, (new,))])
  2025         phases.retractboundary(repo, tr, other.phase(), [new.node()])
  2049         phases.retractboundary(repo, tr, other.phase(), [new.node()])
  2026         tr.close()
       
  2027     finally:
  2050     finally:
  2028         tr.release()
  2051         repo.ui.restoreconfig(emtpycommitallowed)
  2029 
  2052 
  2030 def divergentdata(ctx):
  2053 def divergentdata(ctx):
  2031     """return base, other part of a conflict
  2054     """return base, other part of a conflict
  2032 
  2055 
  2033     This only return the first one.
  2056     This only return the first one.
  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)
  2103 @command('^next',
  2131 @command('^next',
  2104          [('B', 'move-bookmark', False,
  2132          [('B', 'move-bookmark', False,
  2105              _('move active bookmark after update')),
  2133              _('move active bookmark after update')),
  2106           ('', 'merge', False, _('bring uncommitted change along')),
  2134           ('', 'merge', False, _('bring uncommitted change along')),
  2107           ('', 'evolve', False, _('evolve the next changeset if necessary')),
  2135           ('', 'evolve', False, _('evolve the next changeset if necessary')),
  2108           ('n', 'dry-run', False, _('do not perform actions, just print what would be done'))],
  2136           ('n', 'dry-run', False,
  2109          '[OPTION]...')
  2137               _('do not perform actions, just print what would be done'))],
       
  2138               '[OPTION]...')
  2110 def cmdnext(ui, repo, **opts):
  2139 def cmdnext(ui, repo, **opts):
  2111     """update to next child revision
  2140     """update to next child revision
  2112 
  2141 
  2113     Use the ``--evolve`` flag to evolve unstable children on demand.
  2142     Use the ``--evolve`` flag to evolve unstable children on demand.
  2114 
  2143 
  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()
  2632     finally:
  2672     finally:
  2633         lockmod.release(tr, lock, wlock)
  2673         lockmod.release(tr, lock, wlock)
  2634 
  2674 
  2635 @eh.wrapcommand('commit')
  2675 @eh.wrapcommand('commit')
  2636 def commitwrapper(orig, ui, repo, *arg, **kwargs):
  2676 def commitwrapper(orig, ui, repo, *arg, **kwargs):
       
  2677     tr = None
  2637     if kwargs.get('amend', False):
  2678     if kwargs.get('amend', False):
  2638         wlock = lock = None
  2679         wlock = lock = None
  2639     else:
  2680     else:
  2640         wlock = repo.wlock()
  2681         wlock = repo.wlock()
  2641         lock = repo.lock()
  2682         lock = repo.lock()
  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')
  2696         disallowunstable = not obsolete.isenabled(repo,
  2740         disallowunstable = not obsolete.isenabled(repo,
  2697                                                   obsolete.allowunstableopt)
  2741                                                   obsolete.allowunstableopt)
  2698         if disallowunstable:
  2742         if disallowunstable:
  2699             # XXX We should check head revs
  2743             # XXX We should check head revs
  2700             if repo.revs("(%d::) - %d", rev, rev):
  2744             if repo.revs("(%d::) - %d", rev, rev):
  2701                 raise util.Abort(_("cannot split commit: %s not a head") % ctx)
  2745                 raise error.Abort(_("cannot split commit: %s not a head") % ctx)
  2702 
  2746 
  2703         if len(ctx.parents()) > 1:
  2747         if len(ctx.parents()) > 1:
  2704             raise util.Abort(_("cannot split merge commits"))
  2748             raise error.Abort(_("cannot split merge commits"))
  2705         prev = ctx.p1()
  2749         prev = ctx.p1()
  2706         bmupdate = _bookmarksupdater(repo, ctx.node(), tr)
  2750         bmupdate = _bookmarksupdater(repo, ctx.node(), tr)
  2707         bookactive = bmactive(repo)
  2751         bookactive = bmactive(repo)
  2708         if bookactive is not None:
  2752         if bookactive is not None:
  2709             repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo))
  2753             repo.ui.status(_("(leaving bookmark %s)\n") % bmactive(repo))
  2712 
  2756 
  2713         commands.revert(ui, repo, rev=r, all=True)
  2757         commands.revert(ui, repo, rev=r, all=True)
  2714         def haschanges():
  2758         def haschanges():
  2715             modified, added, removed, deleted = repo.status()[:4]
  2759             modified, added, removed, deleted = repo.status()[:4]
  2716             return modified or added or removed or deleted
  2760             return modified or added or removed or deleted
       
  2761         msg = 'HG: Please, edit the original changeset description.\n\n'
       
  2762         msg += ctx.description()
       
  2763         opts['message'] = msg
       
  2764         opts['edit'] = True
  2717         while haschanges():
  2765         while haschanges():
  2718             pats = ()
  2766             pats = ()
  2719             cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
  2767             cmdutil.dorecord(ui, repo, commands.commit, 'commit', False,
  2720                              cmdutil.recordfilter, *pats, **opts)
  2768                              cmdutil.recordfilter, *pats, **opts)
  2721             # TODO: Does no seem like the best way to do this
  2769             # TODO: Does no seem like the best way to do this
  2725                 if ui.prompt('Done splitting? [yN]', default='n') == 'y':
  2773                 if ui.prompt('Done splitting? [yN]', default='n') == 'y':
  2726                     commands.commit(ui, repo, **opts)
  2774                     commands.commit(ui, repo, **opts)
  2727                     newcommits.append(repo['.'])
  2775                     newcommits.append(repo['.'])
  2728                     break
  2776                     break
  2729             else:
  2777             else:
  2730                 ui.status("no more change to split\n")
  2778                 ui.status(_("no more change to split\n"))
  2731 
  2779 
  2732         tip = repo[newcommits[-1]]
  2780         tip = repo[newcommits[-1]]
  2733         bmupdate(tip.node())
  2781         bmupdate(tip.node())
  2734         if bookactive is not None:
  2782         if bookactive is not None:
  2735             bmactivate(repo, bookactive)
  2783             bmactivate(repo, bookactive)
  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()
  2916                          (c.rev(), c.description()) for c in allctx]
  2996                          (c.rev(), c.description()) for c in allctx]
  2917                 commitopts['message'] =  "\n".join(msgs)
  2997                 commitopts['message'] =  "\n".join(msgs)
  2918                 commitopts['edit'] = True
  2998                 commitopts['edit'] = True
  2919 
  2999 
  2920             newid, unusedvariable = rewrite(repo, root, allctx, head,
  3000             newid, unusedvariable = rewrite(repo, root, allctx, head,
  2921                                             [root.p1().node(), root.p2().node()],
  3001                                             [root.p1().node(),
       
  3002                                              root.p2().node()],
  2922                                             commitopts=commitopts)
  3003                                             commitopts=commitopts)
  2923             phases.retractboundary(repo, tr, targetphase, [newid])
  3004             phases.retractboundary(repo, tr, targetphase, [newid])
  2924             obsolete.createmarkers(repo, [(ctx, (repo[newid],))
  3005             obsolete.createmarkers(repo, [(ctx, (repo[newid],))
  2925                                  for ctx in allctx])
  3006                                  for ctx in allctx])
  2926             tr.close()
  3007             tr.close()
  3024 
  3105 
  3025         common = []
  3106         common = []
  3026         obsexcmsg(repo.ui, "looking for common markers in %i nodes\n"
  3107         obsexcmsg(repo.ui, "looking for common markers in %i nodes\n"
  3027                            % len(revs))
  3108                            % len(revs))
  3028         commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads))
  3109         commonrevs = list(unfi.revs('::%ln', pushop.outgoing.commonheads))
  3029         common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote, commonrevs)
  3110         common = findcommonobsmarkers(pushop.ui, unfi, pushop.remote,
       
  3111                                       commonrevs)
  3030 
  3112 
  3031         revs = list(unfi.revs('%ld - (::%ln)', revs, common))
  3113         revs = list(unfi.revs('%ld - (::%ln)', revs, common))
  3032         nodes = [cl.node(r) for r in revs]
  3114         nodes = [cl.node(r) for r in revs]
  3033         if nodes:
  3115         if nodes:
  3034             obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n"
  3116             obsexcmsg(repo.ui, "computing markers relevant to %i nodes\n"
  3056         return discocapabilities(oldcap, repo, proto)
  3138         return discocapabilities(oldcap, repo, proto)
  3057     wireproto.commands['capabilities'] = (newcap, args)
  3139     wireproto.commands['capabilities'] = (newcap, args)
  3058     wireproto.commands['evoext_obshash'] = (srv_obshash, 'nodes')
  3140     wireproto.commands['evoext_obshash'] = (srv_obshash, 'nodes')
  3059     wireproto.commands['evoext_obshash1'] = (srv_obshash1, 'nodes')
  3141     wireproto.commands['evoext_obshash1'] = (srv_obshash1, 'nodes')
  3060     if getattr(exchange, '_pushdiscoveryobsmarkers', None) is None:
  3142     if getattr(exchange, '_pushdiscoveryobsmarkers', None) is None:
  3061         ui.warn('evolve: your mercurial version is too old\n'
  3143         ui.warn(_('evolve: your mercurial version is too old\n'
  3062                 'evolve: (running in degraded mode, push will includes all markers)\n')
  3144                   'evolve: (running in degraded mode, push will '
       
  3145                   'includes all markers)\n'))
  3063     else:
  3146     else:
  3064         olddisco = exchange.pushdiscoverymapping['obsmarker']
  3147         olddisco = exchange.pushdiscoverymapping['obsmarker']
  3065         def newdisco(pushop):
  3148         def newdisco(pushop):
  3066             _pushdiscoveryobsmarkers(olddisco, pushop)
  3149             _pushdiscoveryobsmarkers(olddisco, pushop)
  3067         exchange.pushdiscoverymapping['obsmarker'] = newdisco
  3150         exchange.pushdiscoverymapping['obsmarker'] = newdisco
  3084 
  3167 
  3085 def srv_obshash(repo, proto, nodes):
  3168 def srv_obshash(repo, proto, nodes):
  3086     return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes)))
  3169     return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes)))
  3087 
  3170 
  3088 def srv_obshash1(repo, proto, nodes):
  3171 def srv_obshash1(repo, proto, nodes):
  3089     return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes), version=1))
  3172     return wireproto.encodelist(_obshash(repo, wireproto.decodelist(nodes),
       
  3173                                 version=1))
  3090 
  3174 
  3091 @eh.addattr(localrepo.localpeer, 'evoext_obshash')
  3175 @eh.addattr(localrepo.localpeer, 'evoext_obshash')
  3092 def local_obshash(peer, nodes):
  3176 def local_obshash(peer, nodes):
  3093     return _obshash(peer._repo, nodes)
  3177     return _obshash(peer._repo, nodes)
  3094 
  3178 
  3121     dag = dagutil.revlogdag(cl)
  3205     dag = dagutil.revlogdag(cl)
  3122     missing = set()
  3206     missing = set()
  3123     common = set()
  3207     common = set()
  3124     undecided = set(probeset)
  3208     undecided = set(probeset)
  3125     totalnb = len(undecided)
  3209     totalnb = len(undecided)
  3126     ui.progress("comparing with other", 0, total=totalnb)
  3210     ui.progress(_("comparing with other"), 0, total=totalnb)
  3127     _takefullsample = setdiscovery._takefullsample
  3211     _takefullsample = setdiscovery._takefullsample
  3128     if remote.capable('_evoext_obshash_1'):
  3212     if remote.capable('_evoext_obshash_1'):
  3129         getremotehash = remote.evoext_obshash1
  3213         getremotehash = remote.evoext_obshash1
  3130         localhash = _obsrelsethashtreefm1(local)
  3214         localhash = _obsrelsethashtreefm1(local)
  3131     else:
  3215     else:
  3139             sample = set(undecided)
  3223             sample = set(undecided)
  3140         else:
  3224         else:
  3141             sample = _takefullsample(dag, undecided, size=fullsamplesize)
  3225             sample = _takefullsample(dag, undecided, size=fullsamplesize)
  3142 
  3226 
  3143         roundtrips += 1
  3227         roundtrips += 1
  3144         ui.progress("comparing with other", totalnb - len(undecided),
  3228         ui.progress(_("comparing with other"), totalnb - len(undecided),
  3145                     total=totalnb)
  3229                     total=totalnb)
  3146         ui.debug("query %i; still undecided: %i, sample size is: %i\n"
  3230         ui.debug("query %i; still undecided: %i, sample size is: %i\n"
  3147                  % (roundtrips, len(undecided), len(sample)))
  3231                  % (roundtrips, len(undecided), len(sample)))
  3148         # indices between sample and externalized version must match
  3232         # indices between sample and externalized version must match
  3149         sample = list(sample)
  3233         sample = list(sample)
  3160 
  3244 
  3161         undecided.difference_update(missing)
  3245         undecided.difference_update(missing)
  3162         undecided.difference_update(common)
  3246         undecided.difference_update(common)
  3163 
  3247 
  3164 
  3248 
  3165     ui.progress("comparing with other", None, total=totalnb)
  3249     ui.progress(_("comparing with other"), None, total=totalnb)
  3166     result = dag.headsetofconnecteds(common)
  3250     result = dag.headsetofconnecteds(common)
  3167     ui.debug("%d total queries\n" % roundtrips)
  3251     ui.debug("%d total queries\n" % roundtrips)
  3168 
  3252 
  3169     if not result:
  3253     if not result:
  3170         return set([nullid])
  3254         return set([nullid])
  3224             remote.evoext_pushobsmarkers_0(obsdata)
  3308             remote.evoext_pushobsmarkers_0(obsdata)
  3225             obsexcprg(repo.ui, None)
  3309             obsexcprg(repo.ui, None)
  3226         else:
  3310         else:
  3227             rslts = []
  3311             rslts = []
  3228             remotedata = _pushkeyescape(markers).items()
  3312             remotedata = _pushkeyescape(markers).items()
  3229             totalbytes = sum(len(d) for k,d in remotedata)
  3313             totalbytes = sum(len(d) for k, d in remotedata)
  3230             sentbytes = 0
  3314             sentbytes = 0
  3231             obsexcmsg(repo.ui, "pushing %i obsolescence markers in %i pushkey payload (%i bytes)\n"
  3315             obsexcmsg(repo.ui, "pushing %i obsolescence markers in %i "
  3232                                 % (len(markers), len(remotedata), totalbytes),
  3316                                "pushkey payload (%i bytes)\n"
       
  3317                                % (len(markers), len(remotedata), totalbytes),
  3233                       True)
  3318                       True)
  3234             for key, data in remotedata:
  3319             for key, data in remotedata:
  3235                 obsexcprg(repo.ui, sentbytes, item=key, unit="bytes",
  3320                 obsexcprg(repo.ui, sentbytes, item=key, unit="bytes",
  3236                           total=totalbytes)
  3321                           total=totalbytes)
  3237                 rslts.append(remote.pushkey('obsolete', key, '', data))
  3322                 rslts.append(remote.pushkey('obsolete', key, '', data))
  3269 
  3354 
  3270         for l in vals[1].splitlines(True):
  3355         for l in vals[1].splitlines(True):
  3271             if l.strip():
  3356             if l.strip():
  3272                 self.ui.status(_('remote: '), l)
  3357                 self.ui.status(_('remote: '), l)
  3273         return vals[0]
  3358         return vals[0]
  3274     except socket.error, err:
  3359     except socket.error as err:
  3275         if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
  3360         if err.args[0] in (errno.ECONNRESET, errno.EPIPE):
  3276             raise util.Abort(_('push failed: %s') % err.args[1])
  3361             raise error.Abort(_('push failed: %s') % err.args[1])
  3277         raise util.Abort(err.args[1])
  3362         raise error.Abort(err.args[1])
  3278 
  3363 
  3279 @eh.wrapfunction(localrepo.localrepository, '_restrictcapabilities')
  3364 @eh.wrapfunction(localrepo.localrepository, '_restrictcapabilities')
  3280 def local_pushobsmarker_capabilities(orig, repo, caps):
  3365 def local_pushobsmarker_capabilities(orig, repo, caps):
  3281     caps = orig(repo, caps)
  3366     caps = orig(repo, caps)
  3282     caps.add('_evoext_pushobsmarkers_0')
  3367     caps.add('_evoext_pushobsmarkers_0')
  3331     gboptsmap['evo_obscommon'] = 'nodes'
  3416     gboptsmap['evo_obscommon'] = 'nodes'
  3332 
  3417 
  3333 @eh.wrapfunction(exchange, '_pullbundle2extraprepare')
  3418 @eh.wrapfunction(exchange, '_pullbundle2extraprepare')
  3334 def _addobscommontob2pull(orig, pullop, kwargs):
  3419 def _addobscommontob2pull(orig, pullop, kwargs):
  3335     ret = orig(pullop, kwargs)
  3420     ret = orig(pullop, kwargs)
  3336     if 'obsmarkers' in kwargs and pullop.remote.capable('_evoext_getbundle_obscommon'):
  3421     if ('obsmarkers' in kwargs and
       
  3422         pullop.remote.capable('_evoext_getbundle_obscommon')):
  3337         boundaries = _buildpullobsmarkersboundaries(pullop)
  3423         boundaries = _buildpullobsmarkersboundaries(pullop)
  3338         common = boundaries['common']
  3424         common = boundaries['common']
  3339         if common != [nullid]:
  3425         if common != [nullid]:
  3340             kwargs['evo_obscommon'] = common
  3426             kwargs['evo_obscommon'] = common
  3341     return ret
  3427     return ret
  3444     current = 0
  3530     current = 0
  3445     data = StringIO()
  3531     data = StringIO()
  3446     ui = self.ui
  3532     ui = self.ui
  3447     obsexcprg(ui, current, unit="bytes", total=length)
  3533     obsexcprg(ui, current, unit="bytes", total=length)
  3448     while current < length:
  3534     while current < length:
  3449         readsize = min(length-current, chunk)
  3535         readsize = min(length - current, chunk)
  3450         data.write(f.read(readsize))
  3536         data.write(f.read(readsize))
  3451         current += readsize
  3537         current += readsize
  3452         obsexcprg(ui, current, unit="bytes", total=length)
  3538         obsexcprg(ui, current, unit="bytes", total=length)
  3453     obsexcprg(ui, None)
  3539     obsexcprg(ui, None)
  3454     data.seek(0)
  3540     data.seek(0)
  3479 
  3565 
  3480 def _obsrelsethashtree(repo, encodeonemarker):
  3566 def _obsrelsethashtree(repo, encodeonemarker):
  3481     cache = []
  3567     cache = []
  3482     unfi = repo.unfiltered()
  3568     unfi = repo.unfiltered()
  3483     markercache = {}
  3569     markercache = {}
  3484     repo.ui.progress("preparing locally", 0, total=len(unfi))
  3570     repo.ui.progress(_("preparing locally"), 0, total=len(unfi))
  3485     for i in unfi:
  3571     for i in unfi:
  3486         ctx = unfi[i]
  3572         ctx = unfi[i]
  3487         entry = 0
  3573         entry = 0
  3488         sha = util.sha1()
  3574         sha = util.sha1()
  3489         # add data from p1
  3575         # add data from p1
  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 
  3547     """
  3632     """
  3548     if 'debugobsconvert' in sys.argv:
  3633     if 'debugobsconvert' in sys.argv:
  3549         return
  3634         return
  3550     for mark in markers:
  3635     for mark in markers:
  3551         if node.nullid in mark[1]:
  3636         if node.nullid in mark[1]:
  3552             raise util.Abort(_('bad obsolescence marker detected: '
  3637             raise error.Abort(_('bad obsolescence marker detected: '
  3553                                'invalid successors nullid'),
  3638                                'invalid successors nullid'),
  3554                              hint=_('You should run `hg debugobsconvert`'))
  3639                              hint=_('You should run `hg debugobsconvert`'))
  3555 
  3640 
  3556 @command(
  3641 @command(
  3557     'debugobsconvert',
  3642     'debugobsconvert',
  3559     '')
  3644     '')
  3560 def debugobsconvert(ui, repo, new_format):
  3645 def debugobsconvert(ui, repo, new_format):
  3561     origmarkers = repo.obsstore._all  # settle version
  3646     origmarkers = repo.obsstore._all  # settle version
  3562     if new_format == repo.obsstore._version:
  3647     if new_format == repo.obsstore._version:
  3563         msg = _('New format is the same as the old format, not upgrading!')
  3648         msg = _('New format is the same as the old format, not upgrading!')
  3564         raise util.Abort(msg)
  3649         raise error.Abort(msg)
  3565     f = repo.svfs('obsstore', 'wb', atomictemp=True)
  3650     f = repo.svfs('obsstore', 'wb', atomictemp=True)
  3566     known = set()
  3651     known = set()
  3567     markers = []
  3652     markers = []
  3568     for m in origmarkers:
  3653     for m in origmarkers:
  3569         # filter out invalid markers
  3654         # filter out invalid markers
  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