558 if len(revs) != 1: |
558 if len(revs) != 1: |
559 raise util.Abort( |
559 raise util.Abort( |
560 _('histedit requires exactly one ancestor revision')) |
560 _('histedit requires exactly one ancestor revision')) |
561 |
561 |
562 |
562 |
|
563 replacements = [] |
|
564 keep = opts.get('keep', False) |
|
565 |
|
566 # rebuild state |
563 if goal == 'continue': |
567 if goal == 'continue': |
564 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) |
568 state = readstate(repo) |
565 parentctx = repo[parentctxnode] |
569 parentctx = repo[state.parentctxnode] |
566 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, rules, opts) |
570 parentctx, repl = bootstrapcontinue(ui, repo, parentctx, state.rules, |
567 replacements.extend(repl) |
571 opts) |
|
572 state.replacements.extend(repl) |
|
573 state.parentctxnode = parentctx.node() |
568 elif goal == 'abort': |
574 elif goal == 'abort': |
569 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) |
575 state = readstate(repo) |
570 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, replacements) |
576 mapping, tmpnodes, leafs, _ntm = processreplacement(repo, |
571 ui.debug('restore wc to old parent %s\n' % node.short(topmost)) |
577 state.replacements) |
|
578 ui.debug('restore wc to old parent %s\n' % node.short(state.topmost)) |
572 # check whether we should update away |
579 # check whether we should update away |
573 parentnodes = [c.node() for c in repo[None].parents()] |
580 parentnodes = [c.node() for c in repo[None].parents()] |
574 for n in leafs | set([parentctxnode]): |
581 for n in leafs | set([state.parentctxnode]): |
575 if n in parentnodes: |
582 if n in parentnodes: |
576 hg.clean(repo, topmost) |
583 hg.clean(repo, state.topmost) |
577 break |
584 break |
578 else: |
585 else: |
579 pass |
586 pass |
580 cleanupnode(ui, repo, 'created', tmpnodes) |
587 cleanupnode(ui, repo, 'created', tmpnodes) |
581 cleanupnode(ui, repo, 'temp', leafs) |
588 cleanupnode(ui, repo, 'temp', leafs) |
597 if len(rr) != 1: |
604 if len(rr) != 1: |
598 raise util.Abort(_('The specified revisions must have ' |
605 raise util.Abort(_('The specified revisions must have ' |
599 'exactly one common root')) |
606 'exactly one common root')) |
600 root = rr[0].node() |
607 root = rr[0].node() |
601 |
608 |
602 keep = opts.get('keep', False) |
|
603 revs = between(repo, root, topmost, keep) |
609 revs = between(repo, root, topmost, keep) |
604 if not revs: |
610 if not revs: |
605 raise util.Abort(_('%s is not an ancestor of working directory') % |
611 raise util.Abort(_('%s is not an ancestor of working directory') % |
606 node.short(root)) |
612 node.short(root)) |
607 |
613 |
627 rules = [l for l in (r.strip() for r in rules.splitlines()) |
633 rules = [l for l in (r.strip() for r in rules.splitlines()) |
628 if l and not l.startswith('#')] |
634 if l and not l.startswith('#')] |
629 rules = verifyrules(rules, repo, ctxs) |
635 rules = verifyrules(rules, repo, ctxs) |
630 |
636 |
631 parentctx = repo[root].parents()[0] |
637 parentctx = repo[root].parents()[0] |
632 replacements = [] |
638 |
633 |
639 state = histeditstate(repo, parentctx.node(), rules, keep, |
634 |
640 topmost, replacements) |
635 while rules: |
641 |
636 writestate(repo, parentctx.node(), rules, keep, topmost, replacements) |
642 while state.rules: |
637 action, ha = rules.pop(0) |
643 state.write() |
|
644 action, ha = state.rules.pop(0) |
638 ui.debug('histedit: processing %s %s\n' % (action, ha)) |
645 ui.debug('histedit: processing %s %s\n' % (action, ha)) |
639 actfunc = actiontable[action] |
646 actfunc = actiontable[action] |
640 parentctx, replacement_ = actfunc(ui, repo, parentctx, ha, opts) |
647 parentctx = repo[state.parentctxnode] |
641 replacements.extend(replacement_) |
648 parentctx, replacement_ = actfunc(ui, repo, parentctx, |
642 |
649 ha, opts) |
643 hg.update(repo, parentctx.node()) |
650 state.parentctxnode = parentctx.node() |
644 |
651 state.replacements.extend(replacement_) |
645 mapping, tmpnodes, created, ntm = processreplacement(repo, replacements) |
652 |
|
653 hg.update(repo, state.parentctxnode) |
|
654 |
|
655 mapping, tmpnodes, created, ntm = processreplacement(repo, |
|
656 state.replacements) |
646 if mapping: |
657 if mapping: |
647 for prec, succs in mapping.iteritems(): |
658 for prec, succs in mapping.iteritems(): |
648 if not succs: |
659 if not succs: |
649 ui.debug('histedit: %s is dropped\n' % node.short(prec)) |
660 ui.debug('histedit: %s is dropped\n' % node.short(prec)) |
650 else: |
661 else: |
655 for n in succs[1:]: |
666 for n in succs[1:]: |
656 ui.debug(m % node.short(n)) |
667 ui.debug(m % node.short(n)) |
657 |
668 |
658 if not keep: |
669 if not keep: |
659 if mapping: |
670 if mapping: |
660 movebookmarks(ui, repo, mapping, topmost, ntm) |
671 movebookmarks(ui, repo, mapping, state.topmost, ntm) |
661 # TODO update mq state |
672 # TODO update mq state |
662 if obsolete.isenabled(repo, obsolete.createmarkersopt): |
673 if obsolete.isenabled(repo, obsolete.createmarkersopt): |
663 markers = [] |
674 markers = [] |
664 # sort by revision number because it sound "right" |
675 # sort by revision number because it sound "right" |
665 for prec in sorted(mapping, key=repo.changelog.rev): |
676 for prec in sorted(mapping, key=repo.changelog.rev): |
765 root = ctxs[0] # list is already sorted by repo.set |
776 root = ctxs[0] # list is already sorted by repo.set |
766 if not root.mutable(): |
777 if not root.mutable(): |
767 raise util.Abort(_('cannot edit immutable changeset: %s') % root) |
778 raise util.Abort(_('cannot edit immutable changeset: %s') % root) |
768 return [c.node() for c in ctxs] |
779 return [c.node() for c in ctxs] |
769 |
780 |
770 |
|
771 def writestate(repo, parentnode, rules, keep, topmost, replacements): |
|
772 fp = open(os.path.join(repo.path, 'histedit-state'), 'w') |
|
773 pickle.dump((parentnode, rules, keep, topmost, replacements), fp) |
|
774 fp.close() |
|
775 |
|
776 def readstate(repo): |
781 def readstate(repo): |
777 """Returns a tuple of (parentnode, rules, keep, topmost, replacements). |
782 """Reads a state from file and returns a histeditstate object |
778 """ |
783 """ |
779 try: |
784 try: |
780 fp = open(os.path.join(repo.path, 'histedit-state')) |
785 fp = repo.vfs('histedit-state', 'r') |
781 except IOError, err: |
786 except IOError, err: |
782 if err.errno != errno.ENOENT: |
787 if err.errno != errno.ENOENT: |
783 raise |
788 raise |
784 raise util.Abort(_('no histedit in progress')) |
789 raise util.Abort(_('no histedit in progress')) |
785 return pickle.load(fp) |
790 |
786 |
791 (parentctxnode, rules, keep, topmost, replacements) = pickle.load(fp) |
|
792 |
|
793 return histeditstate(repo, parentctxnode, rules, |
|
794 keep, topmost, replacements) |
787 |
795 |
788 def makedesc(c): |
796 def makedesc(c): |
789 """build a initial action line for a ctx `c` |
797 """build a initial action line for a ctx `c` |
790 |
798 |
791 line are in the form: |
799 line are in the form: |
948 release(lock) |
956 release(lock) |
949 |
957 |
950 def summaryhook(ui, repo): |
958 def summaryhook(ui, repo): |
951 if not os.path.exists(repo.join('histedit-state')): |
959 if not os.path.exists(repo.join('histedit-state')): |
952 return |
960 return |
953 (parentctxnode, rules, keep, topmost, replacements) = readstate(repo) |
961 state = readstate(repo) |
954 if rules: |
962 if state.rules: |
955 # i18n: column positioning for "hg summary" |
963 # i18n: column positioning for "hg summary" |
956 ui.write(_('hist: %s (histedit --continue)\n') % |
964 ui.write(_('hist: %s (histedit --continue)\n') % |
957 (ui.label(_('%d remaining'), 'histedit.remaining') % |
965 (ui.label(_('%d remaining'), 'histedit.remaining') % |
958 len(rules))) |
966 len(state.rules))) |
959 |
967 |
960 def extsetup(ui): |
968 def extsetup(ui): |
961 cmdutil.summaryhooks.add('histedit', summaryhook) |
969 cmdutil.summaryhooks.add('histedit', summaryhook) |
962 cmdutil.unfinishedstates.append( |
970 cmdutil.unfinishedstates.append( |
963 ['histedit-state', False, True, _('histedit in progress'), |
971 ['histedit-state', False, True, _('histedit in progress'), |