summaryrefslogtreecommitdiff
path: root/hgext/histedit.py
diff options
context:
space:
mode:
Diffstat (limited to 'hgext/histedit.py')
-rw-r--r--hgext/histedit.py715
1 files changed, 0 insertions, 715 deletions
diff --git a/hgext/histedit.py b/hgext/histedit.py
deleted file mode 100644
index 88e0e93..0000000
--- a/hgext/histedit.py
+++ /dev/null
@@ -1,715 +0,0 @@
-# histedit.py - interactive history editing for mercurial
-#
-# Copyright 2009 Augie Fackler <raf@durin42.com>
-#
-# This software may be used and distributed according to the terms of the
-# GNU General Public License version 2 or any later version.
-"""interactive history editing
-
-With this extension installed, Mercurial gains one new command: histedit. Usage
-is as follows, assuming the following history::
-
- @ 3[tip] 7c2fd3b9020c 2009-04-27 18:04 -0500 durin42
- | Add delta
- |
- o 2 030b686bedc4 2009-04-27 18:04 -0500 durin42
- | Add gamma
- |
- o 1 c561b4e977df 2009-04-27 18:04 -0500 durin42
- | Add beta
- |
- o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
- Add alpha
-
-If you were to run ``hg histedit c561b4e977df``, you would see the following
-file open in your editor::
-
- pick c561b4e977df Add beta
- pick 030b686bedc4 Add gamma
- pick 7c2fd3b9020c Add delta
-
- # Edit history between 633536316234 and 7c2fd3b9020c
- #
- # Commands:
- # p, pick = use commit
- # e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit
- # d, drop = remove commit from history
- # m, mess = edit message without changing commit content
- #
-
-In this file, lines beginning with ``#`` are ignored. You must specify a rule
-for each revision in your history. For example, if you had meant to add gamma
-before beta, and then wanted to add delta in the same revision as beta, you
-would reorganize the file to look like this::
-
- pick 030b686bedc4 Add gamma
- pick c561b4e977df Add beta
- fold 7c2fd3b9020c Add delta
-
- # Edit history between 633536316234 and 7c2fd3b9020c
- #
- # Commands:
- # p, pick = use commit
- # e, edit = use commit, but stop for amending
- # f, fold = use commit, but fold into previous commit
- # d, drop = remove commit from history
- # m, mess = edit message without changing commit content
- #
-
-At which point you close the editor and ``histedit`` starts working. When you
-specify a ``fold`` operation, ``histedit`` will open an editor when it folds
-those revisions together, offering you a chance to clean up the commit message::
-
- Add beta
- ***
- Add delta
-
-Edit the commit message to your liking, then close the editor. For
-this example, let's assume that the commit message was changed to
-``Add beta and delta.`` After histedit has run and had a chance to
-remove any old or temporary revisions it needed, the history looks
-like this::
-
- @ 2[tip] 989b4d060121 2009-04-27 18:04 -0500 durin42
- | Add beta and delta.
- |
- o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
- | Add gamma
- |
- o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
- Add alpha
-
-Note that ``histedit`` does *not* remove any revisions (even its own temporary
-ones) until after it has completed all the editing operations, so it will
-probably perform several strip operations when it's done. For the above example,
-it had to run strip twice. Strip can be slow depending on a variety of factors,
-so you might need to be a little patient. You can choose to keep the original
-revisions by passing the ``--keep`` flag.
-
-The ``edit`` operation will drop you back to a command prompt,
-allowing you to edit files freely, or even use ``hg record`` to commit
-some changes as a separate commit. When you're done, any remaining
-uncommitted changes will be committed as well. When done, run ``hg
-histedit --continue`` to finish this step. You'll be prompted for a
-new commit message, but the default commit message will be the
-original message for the ``edit`` ed revision.
-
-The ``message`` operation will give you a chance to revise a commit
-message without changing the contents. It's a shortcut for doing
-``edit`` immediately followed by `hg histedit --continue``.
-
-If ``histedit`` encounters a conflict when moving a revision (while
-handling ``pick`` or ``fold``), it'll stop in a similar manner to
-``edit`` with the difference that it won't prompt you for a commit
-message when done. If you decide at this point that you don't like how
-much work it will be to rearrange history, or that you made a mistake,
-you can use ``hg histedit --abort`` to abandon the new changes you
-have made and return to the state before you attempted to edit your
-history.
-
-If we clone the example repository above and add three more changes, such that
-we have the following history::
-
- @ 6[tip] 038383181893 2009-04-27 18:04 -0500 stefan
- | Add theta
- |
- o 5 140988835471 2009-04-27 18:04 -0500 stefan
- | Add eta
- |
- o 4 122930637314 2009-04-27 18:04 -0500 stefan
- | Add zeta
- |
- o 3 836302820282 2009-04-27 18:04 -0500 stefan
- | Add epsilon
- |
- o 2 989b4d060121 2009-04-27 18:04 -0500 durin42
- | Add beta and delta.
- |
- o 1 081603921c3f 2009-04-27 18:04 -0500 durin42
- | Add gamma
- |
- o 0 d8d2fcd0e319 2009-04-27 18:04 -0500 durin42
- Add alpha
-
-If you run ``hg histedit --outgoing`` on the clone then it is the same
-as running ``hg histedit 836302820282``. If you need plan to push to a
-repository that Mercurial does not detect to be related to the source
-repo, you can add a ``--force`` option.
-"""
-
-try:
- import cPickle as pickle
-except ImportError:
- import pickle
-import tempfile
-import os
-
-from mercurial import bookmarks
-from mercurial import cmdutil
-from mercurial import discovery
-from mercurial import error
-from mercurial import hg
-from mercurial import lock as lockmod
-from mercurial import node
-from mercurial import patch
-from mercurial import repair
-from mercurial import scmutil
-from mercurial import util
-from mercurial.i18n import _
-
-cmdtable = {}
-command = cmdutil.command(cmdtable)
-
-testedwith = 'internal'
-
-editcomment = _("""# Edit history between %s and %s
-#
-# Commands:
-# p, pick = use commit
-# e, edit = use commit, but stop for amending
-# f, fold = use commit, but fold into previous commit (combines N and N-1)
-# d, drop = remove commit from history
-# m, mess = edit message without changing commit content
-#
-""")
-
-def between(repo, old, new, keep):
- revs = [old]
- current = old
- while current != new:
- ctx = repo[current]
- if not keep and len(ctx.children()) > 1:
- raise util.Abort(_('cannot edit history that would orphan nodes'))
- if len(ctx.parents()) != 1 and ctx.parents()[1] != node.nullid:
- raise util.Abort(_("can't edit history with merges"))
- if not ctx.children():
- current = new
- else:
- current = ctx.children()[0].node()
- revs.append(current)
- if len(repo[current].children()) and not keep:
- raise util.Abort(_('cannot edit history that would orphan nodes'))
- return revs
-
-
-def pick(ui, repo, ctx, ha, opts):
- oldctx = repo[ha]
- if oldctx.parents()[0] == ctx:
- ui.debug('node %s unchanged\n' % ha)
- return oldctx, [], [], []
- hg.update(repo, ctx.node())
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- try:
- files = set()
- try:
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- if not files:
- ui.warn(_('%s: empty changeset')
- % node.hex(ha))
- return ctx, [], [], []
- finally:
- os.unlink(patchfile)
- except Exception:
- raise util.Abort(_('Fix up the change and run '
- 'hg histedit --continue'))
- n = repo.commit(text=oldctx.description(), user=oldctx.user(),
- date=oldctx.date(), extra=oldctx.extra())
- return repo[n], [n], [oldctx.node()], []
-
-
-def edit(ui, repo, ctx, ha, opts):
- oldctx = repo[ha]
- hg.update(repo, ctx.node())
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- try:
- files = set()
- try:
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- finally:
- os.unlink(patchfile)
- except Exception:
- pass
- raise util.Abort(_('Make changes as needed, you may commit or record as '
- 'needed now.\nWhen you are finished, run hg'
- ' histedit --continue to resume.'))
-
-def fold(ui, repo, ctx, ha, opts):
- oldctx = repo[ha]
- hg.update(repo, ctx.node())
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- try:
- files = set()
- try:
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- if not files:
- ui.warn(_('%s: empty changeset')
- % node.hex(ha))
- return ctx, [], [], []
- finally:
- os.unlink(patchfile)
- except Exception:
- raise util.Abort(_('Fix up the change and run '
- 'hg histedit --continue'))
- n = repo.commit(text='fold-temp-revision %s' % ha, user=oldctx.user(),
- date=oldctx.date(), extra=oldctx.extra())
- return finishfold(ui, repo, ctx, oldctx, n, opts, [])
-
-def finishfold(ui, repo, ctx, oldctx, newnode, opts, internalchanges):
- parent = ctx.parents()[0].node()
- hg.update(repo, parent)
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, parent, newnode, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- files = set()
- try:
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- finally:
- os.unlink(patchfile)
- newmessage = '\n***\n'.join(
- [ctx.description()] +
- [repo[r].description() for r in internalchanges] +
- [oldctx.description()]) + '\n'
- # If the changesets are from the same author, keep it.
- if ctx.user() == oldctx.user():
- username = ctx.user()
- else:
- username = ui.username()
- newmessage = ui.edit(newmessage, username)
- n = repo.commit(text=newmessage, user=username,
- date=max(ctx.date(), oldctx.date()), extra=oldctx.extra())
- return repo[n], [n], [oldctx.node(), ctx.node()], [newnode]
-
-def drop(ui, repo, ctx, ha, opts):
- return ctx, [], [repo[ha].node()], []
-
-
-def message(ui, repo, ctx, ha, opts):
- oldctx = repo[ha]
- hg.update(repo, ctx.node())
- fd, patchfile = tempfile.mkstemp(prefix='hg-histedit-')
- fp = os.fdopen(fd, 'w')
- diffopts = patch.diffopts(ui, opts)
- diffopts.git = True
- diffopts.ignorews = False
- diffopts.ignorewsamount = False
- diffopts.ignoreblanklines = False
- gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
- for chunk in gen:
- fp.write(chunk)
- fp.close()
- try:
- files = set()
- try:
- patch.patch(ui, repo, patchfile, files=files, eolmode=None)
- finally:
- os.unlink(patchfile)
- except Exception:
- raise util.Abort(_('Fix up the change and run '
- 'hg histedit --continue'))
- message = oldctx.description() + '\n'
- message = ui.edit(message, ui.username())
- new = repo.commit(text=message, user=oldctx.user(), date=oldctx.date(),
- extra=oldctx.extra())
- newctx = repo[new]
- if oldctx.node() != newctx.node():
- return newctx, [new], [oldctx.node()], []
- # We didn't make an edit, so just indicate no replaced nodes
- return newctx, [new], [], []
-
-
-def makedesc(c):
- summary = ''
- if c.description():
- summary = c.description().splitlines()[0]
- line = 'pick %s %d %s' % (c.hex()[:12], c.rev(), summary)
- return line[:80] # trim to 80 chars so it's not stupidly wide in my editor
-
-actiontable = {'p': pick,
- 'pick': pick,
- 'e': edit,
- 'edit': edit,
- 'f': fold,
- 'fold': fold,
- 'd': drop,
- 'drop': drop,
- 'm': message,
- 'mess': message,
- }
-
-@command('histedit',
- [('', 'commands', '',
- _('Read history edits from the specified file.')),
- ('c', 'continue', False, _('continue an edit already in progress')),
- ('k', 'keep', False,
- _("don't strip old nodes after edit is complete")),
- ('', 'abort', False, _('abort an edit in progress')),
- ('o', 'outgoing', False, _('changesets not found in destination')),
- ('f', 'force', False,
- _('force outgoing even for unrelated repositories')),
- ('r', 'rev', [], _('first revision to be edited'))],
- _("[PARENT]"))
-def histedit(ui, repo, *parent, **opts):
- """interactively edit changeset history
- """
- # TODO only abort if we try and histedit mq patches, not just
- # blanket if mq patches are applied somewhere
- mq = getattr(repo, 'mq', None)
- if mq and mq.applied:
- raise util.Abort(_('source has mq patches applied'))
-
- parent = list(parent) + opts.get('rev', [])
- if opts.get('outgoing'):
- if len(parent) > 1:
- raise util.Abort(
- _('only one repo argument allowed with --outgoing'))
- elif parent:
- parent = parent[0]
-
- dest = ui.expandpath(parent or 'default-push', parent or 'default')
- dest, revs = hg.parseurl(dest, None)[:2]
- ui.status(_('comparing with %s\n') % util.hidepassword(dest))
-
- revs, checkout = hg.addbranchrevs(repo, repo, revs, None)
- other = hg.peer(repo, opts, dest)
-
- if revs:
- revs = [repo.lookup(rev) for rev in revs]
-
- parent = discovery.findcommonoutgoing(
- repo, other, [], force=opts.get('force')).missing[0:1]
- else:
- if opts.get('force'):
- raise util.Abort(_('--force only allowed with --outgoing'))
-
- if opts.get('continue', False):
- if len(parent) != 0:
- raise util.Abort(_('no arguments allowed with --continue'))
- (parentctxnode, created, replaced,
- tmpnodes, existing, rules, keep, tip, replacemap) = readstate(repo)
- currentparent, wantnull = repo.dirstate.parents()
- parentctx = repo[parentctxnode]
- # existing is the list of revisions initially considered by
- # histedit. Here we use it to list new changesets, descendants
- # of parentctx without an 'existing' changeset in-between. We
- # also have to exclude 'existing' changesets which were
- # previously dropped.
- descendants = set(c.node() for c in
- repo.set('(%n::) - %n', parentctxnode, parentctxnode))
- existing = set(existing)
- notdropped = set(n for n in existing if n in descendants and
- (n not in replacemap or replacemap[n] in descendants))
- # Discover any nodes the user has added in the interim. We can
- # miss changesets which were dropped and recreated the same.
- newchildren = list(c.node() for c in repo.set(
- 'sort(%ln - (%ln or %ln::))', descendants, existing, notdropped))
- action, currentnode = rules.pop(0)
- if action in ('f', 'fold'):
- tmpnodes.extend(newchildren)
- else:
- created.extend(newchildren)
-
- m, a, r, d = repo.status()[:4]
- oldctx = repo[currentnode]
- message = oldctx.description() + '\n'
- if action in ('e', 'edit', 'm', 'mess'):
- message = ui.edit(message, ui.username())
- elif action in ('f', 'fold'):
- message = 'fold-temp-revision %s' % currentnode
- new = None
- if m or a or r or d:
- new = repo.commit(text=message, user=oldctx.user(),
- date=oldctx.date(), extra=oldctx.extra())
-
- # If we're resuming a fold and we have new changes, mark the
- # replacements and finish the fold. If not, it's more like a
- # drop of the changesets that disappeared, and we can skip
- # this step.
- if action in ('f', 'fold') and (new or newchildren):
- if new:
- tmpnodes.append(new)
- else:
- new = newchildren[-1]
- (parentctx, created_, replaced_, tmpnodes_) = finishfold(
- ui, repo, parentctx, oldctx, new, opts, newchildren)
- replaced.extend(replaced_)
- created.extend(created_)
- tmpnodes.extend(tmpnodes_)
- elif action not in ('d', 'drop'):
- if new != oldctx.node():
- replaced.append(oldctx.node())
- if new:
- if new != oldctx.node():
- created.append(new)
- parentctx = repo[new]
-
- elif opts.get('abort', False):
- if len(parent) != 0:
- raise util.Abort(_('no arguments allowed with --abort'))
- (parentctxnode, created, replaced, tmpnodes,
- existing, rules, keep, tip, replacemap) = readstate(repo)
- ui.debug('restore wc to old tip %s\n' % node.hex(tip))
- hg.clean(repo, tip)
- ui.debug('should strip created nodes %s\n' %
- ', '.join([node.hex(n)[:12] for n in created]))
- ui.debug('should strip temp nodes %s\n' %
- ', '.join([node.hex(n)[:12] for n in tmpnodes]))
- for nodes in (created, tmpnodes):
- lock = None
- try:
- lock = repo.lock()
- for n in reversed(nodes):
- try:
- repair.strip(ui, repo, n)
- except error.LookupError:
- pass
- finally:
- lockmod.release(lock)
- os.unlink(os.path.join(repo.path, 'histedit-state'))
- return
- else:
- cmdutil.bailifchanged(repo)
- if os.path.exists(os.path.join(repo.path, 'histedit-state')):
- raise util.Abort(_('history edit already in progress, try '
- '--continue or --abort'))
-
- tip, empty = repo.dirstate.parents()
-
-
- if len(parent) != 1:
- raise util.Abort(_('histedit requires exactly one parent revision'))
- parent = scmutil.revsingle(repo, parent[0]).node()
-
- keep = opts.get('keep', False)
- revs = between(repo, parent, tip, keep)
-
- ctxs = [repo[r] for r in revs]
- existing = [r.node() for r in ctxs]
- rules = opts.get('commands', '')
- if not rules:
- rules = '\n'.join([makedesc(c) for c in ctxs])
- rules += '\n\n'
- rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12])
- rules = ui.edit(rules, ui.username())
- # Save edit rules in .hg/histedit-last-edit.txt in case
- # the user needs to ask for help after something
- # surprising happens.
- f = open(repo.join('histedit-last-edit.txt'), 'w')
- f.write(rules)
- f.close()
- else:
- f = open(rules)
- rules = f.read()
- f.close()
- rules = [l for l in (r.strip() for r in rules.splitlines())
- if l and not l[0] == '#']
- rules = verifyrules(rules, repo, ctxs)
-
- parentctx = repo[parent].parents()[0]
- keep = opts.get('keep', False)
- replaced = []
- replacemap = {}
- tmpnodes = []
- created = []
-
-
- while rules:
- writestate(repo, parentctx.node(), created, replaced,
- tmpnodes, existing, rules, keep, tip, replacemap)
- action, ha = rules.pop(0)
- (parentctx, created_, replaced_, tmpnodes_) = actiontable[action](
- ui, repo, parentctx, ha, opts)
-
- if replaced_:
- clen, rlen = len(created_), len(replaced_)
- if clen == rlen == 1:
- ui.debug('histedit: exact replacement of %s with %s\n' % (
- node.short(replaced_[0]), node.short(created_[0])))
-
- replacemap[replaced_[0]] = created_[0]
- elif clen > rlen:
- assert rlen == 1, ('unexpected replacement of '
- '%d changes with %d changes' % (rlen, clen))
- # made more changesets than we're replacing
- # TODO synthesize patch names for created patches
- replacemap[replaced_[0]] = created_[-1]
- ui.debug('histedit: created many, assuming %s replaced by %s' %
- (node.short(replaced_[0]), node.short(created_[-1])))
- elif rlen > clen:
- if not created_:
- # This must be a drop. Try and put our metadata on
- # the parent change.
- assert rlen == 1
- r = replaced_[0]
- ui.debug('histedit: %s seems replaced with nothing, '
- 'finding a parent\n' % (node.short(r)))
- pctx = repo[r].parents()[0]
- if pctx.node() in replacemap:
- ui.debug('histedit: parent is already replaced\n')
- replacemap[r] = replacemap[pctx.node()]
- else:
- replacemap[r] = pctx.node()
- ui.debug('histedit: %s best replaced by %s\n' % (
- node.short(r), node.short(replacemap[r])))
- else:
- assert len(created_) == 1
- for r in replaced_:
- ui.debug('histedit: %s replaced by %s\n' % (
- node.short(r), node.short(created_[0])))
- replacemap[r] = created_[0]
- else:
- assert False, (
- 'Unhandled case in replacement mapping! '
- 'replacing %d changes with %d changes' % (rlen, clen))
- created.extend(created_)
- replaced.extend(replaced_)
- tmpnodes.extend(tmpnodes_)
-
- hg.update(repo, parentctx.node())
-
- if not keep:
- if replacemap:
- ui.note(_('histedit: Should update metadata for the following '
- 'changes:\n'))
-
- def copybms(old, new):
- if old in tmpnodes or old in created:
- # can't have any metadata we'd want to update
- return
- while new in replacemap:
- new = replacemap[new]
- ui.note(_('histedit: %s to %s\n') % (node.short(old),
- node.short(new)))
- octx = repo[old]
- marks = octx.bookmarks()
- if marks:
- ui.note(_('histedit: moving bookmarks %s\n') %
- ', '.join(marks))
- for mark in marks:
- repo._bookmarks[mark] = new
- bookmarks.write(repo)
-
- # We assume that bookmarks on the tip should remain
- # tipmost, but bookmarks on non-tip changesets should go
- # to their most reasonable successor. As a result, find
- # the old tip and new tip and copy those bookmarks first,
- # then do the rest of the bookmark copies.
- oldtip = sorted(replacemap.keys(), key=repo.changelog.rev)[-1]
- newtip = sorted(replacemap.values(), key=repo.changelog.rev)[-1]
- copybms(oldtip, newtip)
-
- for old, new in sorted(replacemap.iteritems()):
- copybms(old, new)
- # TODO update mq state
-
- ui.debug('should strip replaced nodes %s\n' %
- ', '.join([node.hex(n)[:12] for n in replaced]))
- lock = None
- try:
- lock = repo.lock()
- for n in sorted(replaced, key=lambda x: repo[x].rev()):
- try:
- repair.strip(ui, repo, n)
- except error.LookupError:
- pass
- finally:
- lockmod.release(lock)
-
- ui.debug('should strip temp nodes %s\n' %
- ', '.join([node.hex(n)[:12] for n in tmpnodes]))
- lock = None
- try:
- lock = repo.lock()
- for n in reversed(tmpnodes):
- try:
- repair.strip(ui, repo, n)
- except error.LookupError:
- pass
- finally:
- lockmod.release(lock)
- os.unlink(os.path.join(repo.path, 'histedit-state'))
- if os.path.exists(repo.sjoin('undo')):
- os.unlink(repo.sjoin('undo'))
-
-
-def writestate(repo, parentctxnode, created, replaced,
- tmpnodes, existing, rules, keep, oldtip, replacemap):
- fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
- pickle.dump((parentctxnode, created, replaced,
- tmpnodes, existing, rules, keep, oldtip, replacemap),
- fp)
- fp.close()
-
-def readstate(repo):
- """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules,
- keep, oldtip, replacemap ).
- """
- fp = open(os.path.join(repo.path, 'histedit-state'))
- return pickle.load(fp)
-
-
-def verifyrules(rules, repo, ctxs):
- """Verify that there exists exactly one edit rule per given changeset.
-
- Will abort if there are to many or too few rules, a malformed rule,
- or a rule on a changeset outside of the user-given range.
- """
- parsed = []
- if len(rules) != len(ctxs):
- raise util.Abort(_('must specify a rule for each changeset once'))
- for r in rules:
- if ' ' not in r:
- raise util.Abort(_('malformed line "%s"') % r)
- action, rest = r.split(' ', 1)
- if ' ' in rest.strip():
- ha, rest = rest.split(' ', 1)
- else:
- ha = r.strip()
- try:
- if repo[ha] not in ctxs:
- raise util.Abort(
- _('may not use changesets other than the ones listed'))
- except error.RepoError:
- raise util.Abort(_('unknown changeset %s listed') % ha)
- if action not in actiontable:
- raise util.Abort(_('unknown action "%s"') % action)
- parsed.append([action, ha])
- return parsed