hgext/histedit.py
changeset 26587 56b2bcea2529
parent 26584 e28102403d1b
child 26696 78aa4392c261
equal deleted inserted replaced
26586:d51c658d3f04 26587:56b2bcea2529
   223         try:
   223         try:
   224             fp = self.repo.vfs('histedit-state', 'r')
   224             fp = self.repo.vfs('histedit-state', 'r')
   225         except IOError as err:
   225         except IOError as err:
   226             if err.errno != errno.ENOENT:
   226             if err.errno != errno.ENOENT:
   227                 raise
   227                 raise
   228             raise util.Abort(_('no histedit in progress'))
   228             raise error.Abort(_('no histedit in progress'))
   229 
   229 
   230         try:
   230         try:
   231             data = pickle.load(fp)
   231             data = pickle.load(fp)
   232             parentctxnode, rules, keep, topmost, replacements = data
   232             parentctxnode, rules, keep, topmost, replacements = data
   233             backupfile = None
   233             backupfile = None
   329         repo = state.repo
   329         repo = state.repo
   330         rulehash = rule.strip().split(' ', 1)[0]
   330         rulehash = rule.strip().split(' ', 1)[0]
   331         try:
   331         try:
   332             node = repo[rulehash].node()
   332             node = repo[rulehash].node()
   333         except error.RepoError:
   333         except error.RepoError:
   334             raise util.Abort(_('unknown changeset %s listed') % rulehash[:12])
   334             raise error.Abort(_('unknown changeset %s listed') % rulehash[:12])
   335         return cls(state, node)
   335         return cls(state, node)
   336 
   336 
   337     def run(self):
   337     def run(self):
   338         """Runs the action. The default behavior is simply apply the action's
   338         """Runs the action. The default behavior is simply apply the action's
   339         rulectx onto the current parentctx."""
   339         rulectx onto the current parentctx."""
   437     ctxs = list(repo.set('%d::%d', first, last))
   437     ctxs = list(repo.set('%d::%d', first, last))
   438     if not ctxs:
   438     if not ctxs:
   439         return None
   439         return None
   440     for c in ctxs:
   440     for c in ctxs:
   441         if not c.mutable():
   441         if not c.mutable():
   442             raise util.Abort(
   442             raise error.Abort(
   443                 _("cannot fold into public change %s") % node.short(c.node()))
   443                 _("cannot fold into public change %s") % node.short(c.node()))
   444     base = first.parents()[0]
   444     base = first.parents()[0]
   445 
   445 
   446     # commit a new version of the old changeset, including the update
   446     # commit a new version of the old changeset, including the update
   447     # collect all files which might be affected
   447     # collect all files which might be affected
   662     if revs:
   662     if revs:
   663         revs = [repo.lookup(rev) for rev in revs]
   663         revs = [repo.lookup(rev) for rev in revs]
   664 
   664 
   665     outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
   665     outgoing = discovery.findcommonoutgoing(repo, other, revs, force=force)
   666     if not outgoing.missing:
   666     if not outgoing.missing:
   667         raise util.Abort(_('no outgoing ancestors'))
   667         raise error.Abort(_('no outgoing ancestors'))
   668     roots = list(repo.revs("roots(%ln)", outgoing.missing))
   668     roots = list(repo.revs("roots(%ln)", outgoing.missing))
   669     if 1 < len(roots):
   669     if 1 < len(roots):
   670         msg = _('there are ambiguous outgoing revisions')
   670         msg = _('there are ambiguous outgoing revisions')
   671         hint = _('see "hg help histedit" for more detail')
   671         hint = _('see "hg help histedit" for more detail')
   672         raise util.Abort(msg, hint=hint)
   672         raise error.Abort(msg, hint=hint)
   673     return repo.lookup(roots[0])
   673     return repo.lookup(roots[0])
   674 
   674 
   675 actiontable = {'p': pick,
   675 actiontable = {'p': pick,
   676                'pick': pick,
   676                'pick': pick,
   677                'e': edit,
   677                'e': edit,
   734 def _histedit(ui, repo, state, *freeargs, **opts):
   734 def _histedit(ui, repo, state, *freeargs, **opts):
   735     # TODO only abort if we try and histedit mq patches, not just
   735     # TODO only abort if we try and histedit mq patches, not just
   736     # blanket if mq patches are applied somewhere
   736     # blanket if mq patches are applied somewhere
   737     mq = getattr(repo, 'mq', None)
   737     mq = getattr(repo, 'mq', None)
   738     if mq and mq.applied:
   738     if mq and mq.applied:
   739         raise util.Abort(_('source has mq patches applied'))
   739         raise error.Abort(_('source has mq patches applied'))
   740 
   740 
   741     # basic argument incompatibility processing
   741     # basic argument incompatibility processing
   742     outg = opts.get('outgoing')
   742     outg = opts.get('outgoing')
   743     cont = opts.get('continue')
   743     cont = opts.get('continue')
   744     editplan = opts.get('edit_plan')
   744     editplan = opts.get('edit_plan')
   746     force = opts.get('force')
   746     force = opts.get('force')
   747     rules = opts.get('commands', '')
   747     rules = opts.get('commands', '')
   748     revs = opts.get('rev', [])
   748     revs = opts.get('rev', [])
   749     goal = 'new' # This invocation goal, in new, continue, abort
   749     goal = 'new' # This invocation goal, in new, continue, abort
   750     if force and not outg:
   750     if force and not outg:
   751         raise util.Abort(_('--force only allowed with --outgoing'))
   751         raise error.Abort(_('--force only allowed with --outgoing'))
   752     if cont:
   752     if cont:
   753         if any((outg, abort, revs, freeargs, rules, editplan)):
   753         if any((outg, abort, revs, freeargs, rules, editplan)):
   754             raise util.Abort(_('no arguments allowed with --continue'))
   754             raise error.Abort(_('no arguments allowed with --continue'))
   755         goal = 'continue'
   755         goal = 'continue'
   756     elif abort:
   756     elif abort:
   757         if any((outg, revs, freeargs, rules, editplan)):
   757         if any((outg, revs, freeargs, rules, editplan)):
   758             raise util.Abort(_('no arguments allowed with --abort'))
   758             raise error.Abort(_('no arguments allowed with --abort'))
   759         goal = 'abort'
   759         goal = 'abort'
   760     elif editplan:
   760     elif editplan:
   761         if any((outg, revs, freeargs)):
   761         if any((outg, revs, freeargs)):
   762             raise util.Abort(_('only --commands argument allowed with '
   762             raise error.Abort(_('only --commands argument allowed with '
   763                                '--edit-plan'))
   763                                '--edit-plan'))
   764         goal = 'edit-plan'
   764         goal = 'edit-plan'
   765     else:
   765     else:
   766         if os.path.exists(os.path.join(repo.path, 'histedit-state')):
   766         if os.path.exists(os.path.join(repo.path, 'histedit-state')):
   767             raise util.Abort(_('history edit already in progress, try '
   767             raise error.Abort(_('history edit already in progress, try '
   768                                '--continue or --abort'))
   768                                '--continue or --abort'))
   769         if outg:
   769         if outg:
   770             if revs:
   770             if revs:
   771                 raise util.Abort(_('no revisions allowed with --outgoing'))
   771                 raise error.Abort(_('no revisions allowed with --outgoing'))
   772             if len(freeargs) > 1:
   772             if len(freeargs) > 1:
   773                 raise util.Abort(
   773                 raise error.Abort(
   774                     _('only one repo argument allowed with --outgoing'))
   774                     _('only one repo argument allowed with --outgoing'))
   775         else:
   775         else:
   776             revs.extend(freeargs)
   776             revs.extend(freeargs)
   777             if len(revs) == 0:
   777             if len(revs) == 0:
   778                 # experimental config: histedit.defaultrev
   778                 # experimental config: histedit.defaultrev
   779                 histeditdefault = ui.config('histedit', 'defaultrev')
   779                 histeditdefault = ui.config('histedit', 'defaultrev')
   780                 if histeditdefault:
   780                 if histeditdefault:
   781                     revs.append(histeditdefault)
   781                     revs.append(histeditdefault)
   782             if len(revs) != 1:
   782             if len(revs) != 1:
   783                 raise util.Abort(
   783                 raise error.Abort(
   784                     _('histedit requires exactly one ancestor revision'))
   784                     _('histedit requires exactly one ancestor revision'))
   785 
   785 
   786 
   786 
   787     replacements = []
   787     replacements = []
   788     state.keep = opts.get('keep', False)
   788     state.keep = opts.get('keep', False)
   854                 remote = None
   854                 remote = None
   855             root = findoutgoing(ui, repo, remote, force, opts)
   855             root = findoutgoing(ui, repo, remote, force, opts)
   856         else:
   856         else:
   857             rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
   857             rr = list(repo.set('roots(%ld)', scmutil.revrange(repo, revs)))
   858             if len(rr) != 1:
   858             if len(rr) != 1:
   859                 raise util.Abort(_('The specified revisions must have '
   859                 raise error.Abort(_('The specified revisions must have '
   860                     'exactly one common root'))
   860                     'exactly one common root'))
   861             root = rr[0].node()
   861             root = rr[0].node()
   862 
   862 
   863         revs = between(repo, root, topmost, state.keep)
   863         revs = between(repo, root, topmost, state.keep)
   864         if not revs:
   864         if not revs:
   865             raise util.Abort(_('%s is not an ancestor of working directory') %
   865             raise error.Abort(_('%s is not an ancestor of working directory') %
   866                              node.short(root))
   866                              node.short(root))
   867 
   867 
   868         ctxs = [repo[r] for r in revs]
   868         ctxs = [repo[r] for r in revs]
   869         if not rules:
   869         if not rules:
   870             comment = editcomment % (node.short(root), node.short(topmost))
   870             comment = editcomment % (node.short(root), node.short(topmost))
   958         s = repo.status()
   958         s = repo.status()
   959         if s.modified or s.added or s.removed or s.deleted:
   959         if s.modified or s.added or s.removed or s.deleted:
   960             actobj.continuedirty()
   960             actobj.continuedirty()
   961             s = repo.status()
   961             s = repo.status()
   962             if s.modified or s.added or s.removed or s.deleted:
   962             if s.modified or s.added or s.removed or s.deleted:
   963                 raise util.Abort(_("working copy still dirty"))
   963                 raise error.Abort(_("working copy still dirty"))
   964 
   964 
   965         parentctx, replacements = actobj.continueclean()
   965         parentctx, replacements = actobj.continueclean()
   966 
   966 
   967         state.parentctxnode = parentctx.node()
   967         state.parentctxnode = parentctx.node()
   968         state.replacements.extend(replacements)
   968         state.replacements.extend(replacements)
   975     When keep is false, the specified set can't have children."""
   975     When keep is false, the specified set can't have children."""
   976     ctxs = list(repo.set('%n::%n', old, new))
   976     ctxs = list(repo.set('%n::%n', old, new))
   977     if ctxs and not keep:
   977     if ctxs and not keep:
   978         if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
   978         if (not obsolete.isenabled(repo, obsolete.allowunstableopt) and
   979             repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
   979             repo.revs('(%ld::) - (%ld)', ctxs, ctxs)):
   980             raise util.Abort(_('cannot edit history that would orphan nodes'))
   980             raise error.Abort(_('cannot edit history that would orphan nodes'))
   981         if repo.revs('(%ld) and merge()', ctxs):
   981         if repo.revs('(%ld) and merge()', ctxs):
   982             raise util.Abort(_('cannot edit history that contains merges'))
   982             raise error.Abort(_('cannot edit history that contains merges'))
   983         root = ctxs[0] # list is already sorted by repo.set
   983         root = ctxs[0] # list is already sorted by repo.set
   984         if not root.mutable():
   984         if not root.mutable():
   985             raise util.Abort(_('cannot edit public changeset: %s') % root,
   985             raise error.Abort(_('cannot edit public changeset: %s') % root,
   986                              hint=_('see "hg help phases" for details'))
   986                              hint=_('see "hg help phases" for details'))
   987     return [c.node() for c in ctxs]
   987     return [c.node() for c in ctxs]
   988 
   988 
   989 def makedesc(repo, action, rev):
   989 def makedesc(repo, action, rev):
   990     """build a initial action line for a ctx
   990     """build a initial action line for a ctx
  1031     parsed = []
  1031     parsed = []
  1032     expected = set(c.hex() for c in ctxs)
  1032     expected = set(c.hex() for c in ctxs)
  1033     seen = set()
  1033     seen = set()
  1034     for r in rules:
  1034     for r in rules:
  1035         if ' ' not in r:
  1035         if ' ' not in r:
  1036             raise util.Abort(_('malformed line "%s"') % r)
  1036             raise error.Abort(_('malformed line "%s"') % r)
  1037         action, rest = r.split(' ', 1)
  1037         action, rest = r.split(' ', 1)
  1038         ha = rest.strip().split(' ', 1)[0]
  1038         ha = rest.strip().split(' ', 1)[0]
  1039         try:
  1039         try:
  1040             ha = repo[ha].hex()
  1040             ha = repo[ha].hex()
  1041         except error.RepoError:
  1041         except error.RepoError:
  1042             raise util.Abort(_('unknown changeset %s listed') % ha[:12])
  1042             raise error.Abort(_('unknown changeset %s listed') % ha[:12])
  1043         if ha not in expected:
  1043         if ha not in expected:
  1044             raise util.Abort(
  1044             raise error.Abort(
  1045                 _('may not use changesets other than the ones listed'))
  1045                 _('may not use changesets other than the ones listed'))
  1046         if ha in seen:
  1046         if ha in seen:
  1047             raise util.Abort(_('duplicated command for changeset %s') %
  1047             raise error.Abort(_('duplicated command for changeset %s') %
  1048                     ha[:12])
  1048                     ha[:12])
  1049         seen.add(ha)
  1049         seen.add(ha)
  1050         if action not in actiontable or action.startswith('_'):
  1050         if action not in actiontable or action.startswith('_'):
  1051             raise util.Abort(_('unknown action "%s"') % action)
  1051             raise error.Abort(_('unknown action "%s"') % action)
  1052         parsed.append([action, ha])
  1052         parsed.append([action, ha])
  1053     missing = sorted(expected - seen)  # sort to stabilize output
  1053     missing = sorted(expected - seen)  # sort to stabilize output
  1054     if missing:
  1054     if missing:
  1055         raise util.Abort(_('missing rules for changeset %s') %
  1055         raise error.Abort(_('missing rules for changeset %s') %
  1056                 missing[0][:12],
  1056                 missing[0][:12],
  1057                 hint=_('do you want to use the drop action?'))
  1057                 hint=_('do you want to use the drop action?'))
  1058     return parsed
  1058     return parsed
  1059 
  1059 
  1060 def newnodestoabort(state):
  1060 def newnodestoabort(state):
  1206         histedit_nodes = set([repo[rulehash].node() for (action, rulehash)
  1206         histedit_nodes = set([repo[rulehash].node() for (action, rulehash)
  1207                              in state.rules if rulehash in repo])
  1207                              in state.rules if rulehash in repo])
  1208         strip_nodes = set([repo[n].node() for n in nodelist])
  1208         strip_nodes = set([repo[n].node() for n in nodelist])
  1209         common_nodes = histedit_nodes & strip_nodes
  1209         common_nodes = histedit_nodes & strip_nodes
  1210         if common_nodes:
  1210         if common_nodes:
  1211             raise util.Abort(_("histedit in progress, can't strip %s")
  1211             raise error.Abort(_("histedit in progress, can't strip %s")
  1212                              % ', '.join(node.short(x) for x in common_nodes))
  1212                              % ', '.join(node.short(x) for x in common_nodes))
  1213     return orig(ui, repo, nodelist, *args, **kwargs)
  1213     return orig(ui, repo, nodelist, *args, **kwargs)
  1214 
  1214 
  1215 extensions.wrapfunction(repair, 'strip', stripwrapper)
  1215 extensions.wrapfunction(repair, 'strip', stripwrapper)
  1216 
  1216