1. Gustavo Picon
  2. MacHg

Commits

Jason Harris  committed b0a0ec0

- Switch to new Mercurial 1.5.4

  • Participants
  • Parent commits f12e51d
  • Branches default

Comments (0)

Files changed (27)

File LocalMercurial/hgext/collapse.py

View file
+# collapse.py - collapse feature for mercurial
+#
+# Copyright 2009 Colin Caughie <c.caughie at indigovision dot com>
+#
+# This software may be used and distributed according to the terms
+# of the GNU General Public License, incorporated herein by reference.
+
+'''collapse multiple revisions into one
+'''
+
+from mercurial import util, repair, merge, cmdutil, commands
+from mercurial.node import nullrev
+from mercurial.i18n import _
+import re
+
+def collapse(ui, repo, **opts):
+    """collapse multiple revisions into one
+
+    Collapse combines multiple consecutive changesets into a single changeset,
+    preserving any descendants of the final changeset. The commit messages for
+    the collapsed changesets are concatenated and may be edited before the
+    collapse is completed.
+    """
+
+    rng = cmdutil.revrange(repo, opts['rev'])
+    if not rng:
+        raise util.Abort(_('no revisions specified'))
+
+    first = rng[0]
+    last = rng[-1]
+    revs = inbetween(repo, first, last)
+
+    if not revs:
+        raise util.Abort(_('revision %s is not an ancestor of revision %s\n') %
+                            (first, last))
+    elif len(revs) == 1:
+        raise util.Abort(_('only one revision specified'))
+
+    ui.debug(_('Collapsing revisions %s\n') % revs)
+
+    for r in revs:
+        if repo[r].user() != ui.username() and not opts['force']:
+            raise util.Abort(_('revision %s does not belong to %s\n') %
+                (r, ui.username()))
+        if r != last:
+            children = repo[r].children()
+            if len(children) > 1:
+                for c in children:
+                    if not c.rev() in revs:
+                        raise util.Abort(_('revision %s has child %s not '
+                            'being collapsed, please rebase\n') % (r, c.rev()))
+        if r != first:
+            parents = repo[r].parents()
+            if len(parents) > 1:
+                for p in parents:
+                    if not p.rev() in revs:
+                        raise util.Abort(_('revision %s has parent %s not '
+                            'being collapsed.') % (r, p.rev()))
+
+    if len(repo[first].parents()) > 1:
+        raise util.Abort(_('start revision %s has multiple parents, '
+            'won\'t collapse.') % first)
+
+    cmdutil.bail_if_changed(repo)
+
+    parent = repo[first].parents()[0]
+    tomove = list(repo.changelog.descendants(last))
+    movemap = dict.fromkeys(tomove, nullrev)
+    ui.debug(_('will move revisions: %s\n') % tomove)
+
+    origparent = repo['.'].rev()
+    collapsed = None
+
+    try:
+        collapsed = makecollapsed(ui, repo, parent, revs, opts)
+        movemap[max(revs)] = collapsed
+        movedescendants(ui, repo, collapsed, tomove, movemap)
+    except:
+        merge.update(repo, repo[origparent].rev(), False, True, False)
+        if collapsed:
+            repair.strip(ui, repo, collapsed.node(), "strip")
+        raise
+
+    if not opts['keep']:
+        ui.debug(_('stripping revision %d\n') % first)
+        repair.strip(ui, repo, repo[first].node(), "strip")
+
+    ui.status(_('collapse completed\n'))
+
+def makecollapsed(ui, repo, parent, revs, opts):
+    'Creates the collapsed revision on top of parent'
+
+    last = max(revs)
+    ui.debug(_('updating to revision %d\n') % parent)
+    merge.update(repo, parent.node(), False, False, False)
+    ui.debug(_('reverting to revision %d\n') % last)
+    commands.revert(ui, repo, rev=last, all=True, date=None)
+    msg = ''
+    if opts['message'] != "" :
+        msg = opts['message']
+    else:
+        first = True
+        for r in revs:
+            if not first:
+                msg += '----------------\n'
+            first = False
+            msg += repo[r].description() + "\n"
+
+        msg += "\nHG: Enter commit message.  Lines beginning with 'HG:' are removed.\n"
+        msg += "HG: Remove all lines to abort the collapse operation.\n"
+
+        if ui.config('ui', 'interactive') != 'off':
+            msg = ui.edit(msg, ui.username())
+
+        pattern = re.compile("^HG:.*\n", re.MULTILINE);
+        msg  = re.sub(pattern, "", msg).strip();
+
+    if not msg:
+        raise util.Abort(_('empty commit message, collapse won\'t proceed'))
+
+    newrev = repo.commit(
+        text=msg,
+        user=repo[last].user(),
+        date=repo[last].date())
+
+    return repo[newrev]
+
+def movedescendants(ui, repo, collapsed, tomove, movemap):
+    'Moves the descendants of the source revisions to the collapsed revision'
+
+    sorted_tomove = list(tomove)
+    sorted_tomove.sort()
+
+    for r in sorted_tomove:
+        ui.debug(_('moving revision %r\n') % r)
+        parents = [p.rev() for p in repo[r].parents()]
+        if len(parents) == 1:
+            ui.debug(_('setting parent to %d\n') % movemap[parents[0]].rev())
+            repo.dirstate.setparents(movemap[parents[0]].node())
+        else:
+            ui.debug(_('setting parents to %d and %d\n') %
+                (movemap[parents[0]].rev(), movemap[parents[1]].rev()))
+            repo.dirstate.setparents(movemap[parents[0]].node(),
+                movemap[parents[1]].node())
+
+        repo.dirstate.write()
+
+        ui.debug(_('reverting to revision %d\n') % r)
+        commands.revert(ui, repo, rev=r, all=True, date=None)
+        newrev = repo.commit(text=repo[r].description(), user=repo[r].user(),
+                            date=repo[r].date())
+        ctx = repo[newrev]
+        movemap[r] = ctx
+
+def inbetween(repo, first, last):
+    'Return all revisions between first and last, inclusive'
+
+    if first == last:
+        return set([first])
+    elif last < first:
+        return set()
+
+    parents = [p.rev() for p in repo[last].parents()]
+
+    if not parents:
+        return set()
+
+    result = inbetween(repo, first, parents[0])
+    if len(parents) == 2:
+        result = result | inbetween(repo, first, parents[1])
+
+    if result:
+        result.add(last)
+
+    return result
+
+cmdtable = {
+"collapse":
+        (collapse,
+        [
+        ('r', 'rev', [], _('revisions to collapse')),
+        ('', 'keep', False, _('keep original revisions')),
+        ('f', 'force', False, _('force collapse of changes from different users')),
+        ('m', 'message', "", _('use <text> as combined commit message'))
+        ],
+        _('hg collapse -r REVS [--keep | --keepbranches]')),
+}

File LocalMercurial/hgext/histedit/Makefile

View file
+PYTHON=python
+
+help:
+	@echo 'Commonly used make targets:'
+	@echo '  tests        - run all tests in the automatic test suite'
+
+all: tests
+
+tests:
+	cd tests && $(PYTHON) run-tests.py --with-hg=`which hg` $(TESTFLAGS)
+
+test-%:
+	cd tests && $(PYTHON) run-tests.py --with-hg=`which hg` $(TESTFLAGS) $@
+.PHONY: help all local build doc clean install install-bin install-doc \
+	install-home install-home-bin install-home-doc dist dist-notests tests \
+	update-pot

File LocalMercurial/hgext/histedit/README

View file
+.. -*-restructuredtext-*-
+
+============
+histedit
+============
+Interactive history editing for Mercurial.
+
+**Warning**: ``histedit`` alters history! It probably won't destroy your data,
+but that chance exists. You should *never* edit already-published history. 
+Caveat Emptor. (You may want to use the --outgoing option to prevent editing
+unpushed history, though the changes may already be in another repository.)
+
+**Warning 2**: If you ``drop`` a change, it's *gone* **forever** (unless you 
+passed ``--keep``). You've been warned!
+
+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
+ #
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+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
+ #
+ 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+
+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.
+
+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.
+
+============
+--outgoing
+============
+Interactive history editing only pushed changes.
+
+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.

File LocalMercurial/hgext/histedit/__init__.py

View file
+"""Interactive history editing.
+
+Inspired by git rebase --interactive.
+"""
+try:
+    import cPickle as pickle
+except ImportError:
+    import pickle
+import tempfile
+import os
+
+from mercurial import cmdutil
+from mercurial import error
+from mercurial import hg
+from mercurial import node
+from mercurial import repair
+from mercurial import patch
+from mercurial import util
+from mercurial import url
+from mercurial.i18n import _
+
+# almost entirely stolen from the git-rebase--interactive.sh source
+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
+#  d, drop = remove commit from history
+#
+"""
+
+def between(repo, old, new):
+    revs = [old, ]
+    current = old
+    while current != new:
+        ctx = repo[current]
+        if 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()):
+        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
+    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
+    for chunk in gen:
+        fp.write(chunk)
+    fp.close()
+    try:
+        files = {}
+        try:
+            patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None)
+            if not files:
+                ui.warn(_('%s: empty changeset')
+                             % node.hex(ha))
+                return ctx, [], [], []
+        finally:
+            files = patch.updatedir(ui, repo, files)
+            os.unlink(patchfile)
+    except Exception, inst:
+        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
+    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
+    for chunk in gen:
+        fp.write(chunk)
+    fp.close()
+    try:
+        files = {}
+        try:
+            patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None)
+        finally:
+            files = patch.updatedir(ui, repo, files)
+            os.unlink(patchfile)
+    except Exception, inst:
+        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
+    gen = patch.diff(repo, oldctx.parents()[0].node(), ha, opts=diffopts)
+    for chunk in gen:
+        fp.write(chunk)
+    fp.close()
+    try:
+        files = {}
+        try:
+            patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None)
+            if not files:
+                ui.warn(_('%s: empty changeset')
+                             % node.hex(ha))
+                return ctx, [], [], []
+        finally:
+            files = patch.updatedir(ui, repo, files)
+            os.unlink(patchfile)
+    except Exception, inst:
+        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
+    gen = patch.diff(repo, parent, newnode, opts=diffopts)
+    for chunk in gen:
+        fp.write(chunk)
+    fp.close()
+    files = {}
+    try:
+        patch.patch(patchfile, ui, cwd=repo.root, files=files, eolmode=None)
+    finally:
+        files = patch.updatedir(ui, repo, files)
+        os.unlink(patchfile)
+    newmessage = '\n***\n'.join(
+        [ctx.description(), ] +
+        [repo[r].description() for r in internalchanges] +
+        [oldctx.description(), ])
+    newmessage = ui.edit(newmessage, ui.username())
+    n = repo.commit(text=newmessage, user=ui.username(), date=max(ctx.date(), oldctx.date()),
+                    extra=oldctx.extra())
+    return repo[n], [n, ], [oldctx.node(), ctx.node() ], [newnode, ] # xxx
+
+def drop(ui, repo, ctx, ha, opts):
+    return ctx, [], [repo[ha].node(), ], []
+
+
+actiontable = {'p': pick,
+               'pick': pick,
+               'e': edit,
+               'edit': edit,
+               'f': fold,
+               'fold': fold,
+               'd': drop,
+               'drop': drop,
+               }
+def histedit(ui, repo, *parent, **opts):
+    """hg histedit <parent>
+    """
+    if opts.get('outgoing'):
+        if len(parent) > 1:
+            raise util.Abort('only one repo argument allowed with --outgoing')
+        elif parent:
+            parent = parent[0]
+
+        parsed = hg.parseurl(ui.expandpath(parent or 'default-push',
+                                           parent or 'default'), ['tip'])
+        dest, revs = parsed[:2]
+        if revs:
+            revs = [repo.lookup(rev) for rev in revs]
+
+        other = hg.repository(ui, dest)
+        ui.status(_('comparing with %s\n') % url.hidepassword(dest))
+        parent = repo.findoutgoing(other, force=opts.get('force'))
+    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, ) = readstate(repo)
+        currentparent, wantnull = repo.dirstate.parents()
+        parentctx = repo[parentctxnode]
+        # discover any nodes the user has added in the interim
+        newchildren = [c for c in parentctx.children()
+                       if c.node() not in existing]
+        action, currentnode = rules.pop(0)
+        while newchildren:
+            if action in ['f', 'fold', ]:
+                tmpnodes.extend([n.node() for n in newchildren])
+            else:
+                created.extend([n.node() for n in newchildren])
+            newchildren = filter(lambda x: x.node() not in existing,
+                                 reduce(lambda x, y: x + y,
+                                        map(lambda r: r.children(),
+                                            newchildren)))
+        m, a, r, d = repo.status()[:4]
+        oldctx = repo[currentnode]
+        message = oldctx.description()
+        if action in ('e', 'edit', ):
+            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 action in ('e', 'edit', 'p', 'pick', ):
+            if new != oldctx.node():
+                replaced.append(oldctx.node())
+            if new:
+                if new != oldctx.node():
+                    created.append(new)
+                parentctx = repo[new]
+        else: # fold
+            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 opts.get('abort', False):
+        if len(parent) != 0:
+            raise util.Abort('no arguments allowed with --abort')
+        (parentctxnode, created, replaced, tmpnodes,
+         existing, rules, keep, tip, ) = 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, ):
+            for n in reversed(nodes):
+                try:
+                    repair.strip(ui, repo, n)
+                except error.LookupError:
+                    pass
+        os.unlink(os.path.join(repo.path, 'histedit-state'))
+        return
+    else:
+        cmdutil.bail_if_changed(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('requires exactly one parent revision')
+        parent = parent[0]
+
+        revs = between(repo, parent, tip)
+
+        ctxs = [repo[r] for r in revs]
+        existing = [r.node() for r in ctxs]
+        rules = '\n'.join([('pick %s %s' % (c.hex()[:12],
+                                         c.description().splitlines()[0]))[:80]
+                           for c in ctxs])
+
+        if opts.get('startingrules', False):
+        	ui.write(rules)
+        	return
+
+        if opts.get('rules') != "" :
+            rules = ''.join(opts['rules'])
+            ui.write(rules)
+        else:
+            rules += editcomment % (node.hex(parent)[:12], node.hex(tip)[:12], )
+            rules = ui.edit(rules, ui.username())
+
+        parentctx = repo[parent].parents()[0]
+
+        rules = [l for l in (r.strip() for r in rules.splitlines()) if l and not l[0] == '#']
+        rules = verifyrules(rules, repo, ctxs)
+        keep = opts.get('keep', False)
+        replaced = []
+        tmpnodes = []
+        created = []
+
+
+    while rules:
+        writestate(repo, parentctx.node(), created, replaced, tmpnodes, existing,
+                   rules, keep, tip)
+        action, ha = rules.pop(0)
+        (parentctx, created_,
+         replaced_, tmpnodes_, ) = actiontable[action](ui, repo,
+                                                       parentctx, ha,
+                                                       opts)
+        created.extend(created_)
+        replaced.extend(replaced_)
+        tmpnodes.extend(tmpnodes_)
+
+    hg.update(repo, parentctx.node())
+
+    if not keep:
+        ui.debug('should strip replaced nodes %s\n' %
+                 ', '.join([node.hex(n)[:12] for n in replaced]))
+        for n in sorted(replaced, lambda x, y: cmp(repo[x].rev(), repo[y].rev())):
+            try:
+                repair.strip(ui, repo, n)
+            except error.LookupError:
+                pass
+
+    ui.debug('should strip temp nodes %s\n' %
+             ', '.join([node.hex(n)[:12] for n in tmpnodes]))
+    for n in reversed(tmpnodes):
+        try:
+            repair.strip(ui, repo, n)
+        except error.LookupError:
+            pass
+    os.unlink(os.path.join(repo.path, 'histedit-state'))
+
+
+def writestate(repo, parentctxnode, created, replaced,
+               tmpnodes, existing, rules, keep, oldtip):
+    fp = open(os.path.join(repo.path, 'histedit-state'), 'w')
+    pickle.dump((parentctxnode, created, replaced,
+                 tmpnodes, existing, rules, keep, oldtip,),
+                fp)
+    fp.close()
+
+def readstate(repo):
+    """Returns a tuple of (parentnode, created, replaced, tmp, existing, rules, keep, oldtip, ).
+    """
+    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 = []
+    first = True
+    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
+
+
+cmdtable = {
+    "histedit":
+        (histedit,
+         [('c', 'continue', False, 'continue an edit already in progress', ),
+          ('k', 'keep', False, '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'),
+          ('',  'startingrules', False, 'issue the starting rules before editing. Useful for gui clients'),
+          ('',  'rules',  '', 'accept rules specified here instead of interactively edited by the user. Useful for gui clients')
+          ],
+         __doc__,
+         ),
+}

File LocalMercurial/hgext/histedit/__init__.pyc

Binary file added.

File LocalMercurial/hgext/histedit/tests/run-tests.py

View file
+#!/usr/bin/env python
+#
+# run-tests.py - Run a set of tests on Mercurial
+#
+# Copyright 2006 Matt Mackall <mpm@selenic.com>
+#
+# This software may be used and distributed according to the terms of the
+# GNU General Public License version 2, incorporated herein by reference.
+
+import difflib
+import errno
+import optparse
+import os
+try:
+    import subprocess
+    subprocess.Popen  # trigger ImportError early
+    closefds = os.name == 'posix'
+    def Popen4(cmd, bufsize=-1):
+        p = subprocess.Popen(cmd, shell=True, bufsize=bufsize,
+                             close_fds=closefds,
+                             stdin=subprocess.PIPE, stdout=subprocess.PIPE,
+                             stderr=subprocess.STDOUT)
+        p.fromchild = p.stdout
+        p.tochild = p.stdin
+        p.childerr = p.stderr
+        return p
+except ImportError:
+    subprocess = None
+    from popen2 import Popen4
+import shutil
+import signal
+import sys
+import tempfile
+import time
+
+# reserved exit code to skip test (used by hghave)
+SKIPPED_STATUS = 80
+SKIPPED_PREFIX = 'skipped: '
+FAILED_PREFIX  = 'hghave check failed: '
+PYTHON = sys.executable
+
+requiredtools = ["python", "diff", "grep", "unzip", "gunzip", "bunzip2", "sed"]
+
+defaults = {
+    'jobs': ('HGTEST_JOBS', 1),
+    'timeout': ('HGTEST_TIMEOUT', 180),
+    'port': ('HGTEST_PORT', 20059),
+}
+
+def parseargs():
+    parser = optparse.OptionParser("%prog [options] [tests]")
+    parser.add_option("-C", "--annotate", action="store_true",
+        help="output files annotated with coverage")
+    parser.add_option("--child", type="int",
+        help="run as child process, summary to given fd")
+    parser.add_option("-c", "--cover", action="store_true",
+        help="print a test coverage report")
+    parser.add_option("-f", "--first", action="store_true",
+        help="exit on the first test failure")
+    parser.add_option("-i", "--interactive", action="store_true",
+        help="prompt to accept changed output")
+    parser.add_option("-j", "--jobs", type="int",
+        help="number of jobs to run in parallel"
+             " (default: $%s or %d)" % defaults['jobs'])
+    parser.add_option("--keep-tmpdir", action="store_true",
+        help="keep temporary directory after running tests"
+             " (best used with --tmpdir)")
+    parser.add_option("-R", "--restart", action="store_true",
+        help="restart at last error")
+    parser.add_option("-p", "--port", type="int",
+        help="port on which servers should listen"
+             " (default: $%s or %d)" % defaults['port'])
+    parser.add_option("-r", "--retest", action="store_true",
+        help="retest failed tests")
+    parser.add_option("-s", "--cover_stdlib", action="store_true",
+        help="print a test coverage report inc. standard libraries")
+    parser.add_option("-t", "--timeout", type="int",
+        help="kill errant tests after TIMEOUT seconds"
+             " (default: $%s or %d)" % defaults['timeout'])
+    parser.add_option("--tmpdir", type="string",
+        help="run tests in the given temporary directory")
+    parser.add_option("-v", "--verbose", action="store_true",
+        help="output verbose messages")
+    parser.add_option("-n", "--nodiff", action="store_true",
+        help="skip showing test changes")
+    parser.add_option("--with-hg", type="string",
+        help="test existing install at given location")
+    parser.add_option("--pure", action="store_true",
+        help="use pure Python code instead of C extensions")
+
+    for option, default in defaults.items():
+        defaults[option] = int(os.environ.get(*default))
+    parser.set_defaults(**defaults)
+    (options, args) = parser.parse_args()
+
+    global vlog
+    options.anycoverage = (options.cover or
+                           options.cover_stdlib or
+                           options.annotate)
+
+    if options.verbose:
+        def vlog(*msg):
+            for m in msg:
+                print m,
+            print
+    else:
+        vlog = lambda *msg: None
+
+    if options.jobs < 1:
+        print >> sys.stderr, 'ERROR: -j/--jobs must be positive'
+        sys.exit(1)
+    if options.interactive and options.jobs > 1:
+        print '(--interactive overrides --jobs)'
+        options.jobs = 1
+
+    return (options, args)
+
+def rename(src, dst):
+    """Like os.rename(), trade atomicity and opened files friendliness
+    for existing destination support.
+    """
+    shutil.copy(src, dst)
+    os.remove(src)
+
+def splitnewlines(text):
+    '''like str.splitlines, but only split on newlines.
+    keep line endings.'''
+    i = 0
+    lines = []
+    while True:
+        n = text.find('\n', i)
+        if n == -1:
+            last = text[i:]
+            if last:
+                lines.append(last)
+            return lines
+        lines.append(text[i:n+1])
+        i = n + 1
+
+def parsehghaveoutput(lines):
+    '''Parse hghave log lines.
+    Return tuple of lists (missing, failed):
+      * the missing/unknown features
+      * the features for which existence check failed'''
+    missing = []
+    failed = []
+    for line in lines:
+        if line.startswith(SKIPPED_PREFIX):
+            line = line.splitlines()[0]
+            missing.append(line[len(SKIPPED_PREFIX):])
+        elif line.startswith(FAILED_PREFIX):
+            line = line.splitlines()[0]
+            failed.append(line[len(FAILED_PREFIX):])
+
+    return missing, failed
+
+def showdiff(expected, output):
+    for line in difflib.unified_diff(expected, output,
+            "Expected output", "Test output"):
+        sys.stdout.write(line)
+
+def findprogram(program):
+    """Search PATH for a executable program"""
+    for p in os.environ.get('PATH', os.defpath).split(os.pathsep):
+        name = os.path.join(p, program)
+        if os.access(name, os.X_OK):
+            return name
+    return None
+
+def checktools():
+    # Before we go any further, check for pre-requisite tools
+    # stuff from coreutils (cat, rm, etc) are not tested
+    for p in requiredtools:
+        if os.name == 'nt':
+            p += '.exe'
+        found = findprogram(p)
+        if found:
+            vlog("# Found prerequisite", p, "at", found)
+        else:
+            print "WARNING: Did not find prerequisite tool: "+p
+
+def cleanup(options):
+    if not options.keep_tmpdir:
+        if options.verbose:
+            print "# Cleaning up HGTMP", HGTMP
+        shutil.rmtree(HGTMP, True)
+
+def usecorrectpython():
+    # some tests run python interpreter. they must use same
+    # interpreter we use or bad things will happen.
+    exedir, exename = os.path.split(sys.executable)
+    if exename == 'python':
+        path = findprogram('python')
+        if os.path.dirname(path) == exedir:
+            return
+    vlog('# Making python executable in test path use correct Python')
+    mypython = os.path.join(BINDIR, 'python')
+    try:
+        os.symlink(sys.executable, mypython)
+    except AttributeError:
+        # windows fallback
+        shutil.copyfile(sys.executable, mypython)
+        shutil.copymode(sys.executable, mypython)
+
+def installhg(options):
+    global PYTHON
+    vlog("# Performing temporary installation of HG")
+    installerrs = os.path.join("tests", "install.err")
+    pure = options.pure and "--pure" or ""
+
+    # Run installer in hg root
+    os.chdir(os.path.join(os.path.dirname(sys.argv[0]), '..'))
+    cmd = ('%s setup.py %s clean --all'
+           ' install --force --prefix="%s" --install-lib="%s"'
+           ' --install-scripts="%s" >%s 2>&1'
+           % (sys.executable, pure, INST, PYTHONDIR, BINDIR, installerrs))
+    vlog("# Running", cmd)
+    if os.system(cmd) == 0:
+        if not options.verbose:
+            os.remove(installerrs)
+    else:
+        f = open(installerrs)
+        for line in f:
+            print line,
+        f.close()
+        sys.exit(1)
+    os.chdir(TESTDIR)
+
+    os.environ["PATH"] = "%s%s%s" % (BINDIR, os.pathsep, os.environ["PATH"])
+
+    pydir = os.pathsep.join([PYTHONDIR, TESTDIR])
+    pythonpath = os.environ.get("PYTHONPATH")
+    if pythonpath:
+        pythonpath = pydir + os.pathsep + pythonpath
+    else:
+        pythonpath = pydir
+    os.environ["PYTHONPATH"] = pythonpath
+
+    usecorrectpython()
+    global hgpkg
+    hgpkg = _hgpath()
+
+    vlog("# Installing dummy diffstat")
+    f = open(os.path.join(BINDIR, 'diffstat'), 'w')
+    f.write('#!' + sys.executable + '\n'
+            'import sys\n'
+            'files = 0\n'
+            'for line in sys.stdin:\n'
+            '    if line.startswith("diff "):\n'
+            '        files += 1\n'
+            'sys.stdout.write("files patched: %d\\n" % files)\n')
+    f.close()
+    os.chmod(os.path.join(BINDIR, 'diffstat'), 0700)
+
+    if options.anycoverage:
+        vlog("# Installing coverage wrapper")
+        os.environ['COVERAGE_FILE'] = COVERAGE_FILE
+        if os.path.exists(COVERAGE_FILE):
+            os.unlink(COVERAGE_FILE)
+        # Create a wrapper script to invoke hg via coverage.py
+        os.rename(os.path.join(BINDIR, "hg"), os.path.join(BINDIR, "_hg.py"))
+        f = open(os.path.join(BINDIR, 'hg'), 'w')
+        f.write('#!' + sys.executable + '\n')
+        f.write('import sys, os; os.execv(sys.executable, [sys.executable, '
+                '"%s", "-x", "%s"] + sys.argv[1:])\n' %
+                (os.path.join(TESTDIR, 'coverage.py'),
+                 os.path.join(BINDIR, '_hg.py')))
+        f.close()
+        os.chmod(os.path.join(BINDIR, 'hg'), 0700)
+        PYTHON = '"%s" "%s" -x' % (sys.executable,
+                                   os.path.join(TESTDIR,'coverage.py'))
+
+def _hgpath():
+    cmd = '%s -c "import mercurial; print mercurial.__path__[0]"'
+    hgpath = os.popen(cmd % PYTHON)
+    path = hgpath.read().strip()
+    hgpath.close()
+    return path
+
+def outputcoverage(options):
+    vlog("# Producing coverage report")
+    omit = [BINDIR, TESTDIR, PYTHONDIR]
+    if not options.cover_stdlib:
+        # Exclude as system paths (ignoring empty strings seen on win)
+        omit += [x for x in sys.path if x != '']
+    omit = ','.join(omit)
+    os.chdir(PYTHONDIR)
+    cmd = '"%s" "%s" -i -r "--omit=%s"' % (
+        sys.executable, os.path.join(TESTDIR, 'coverage.py'), omit)
+    vlog("# Running: "+cmd)
+    os.system(cmd)
+    if options.annotate:
+        adir = os.path.join(TESTDIR, 'annotated')
+        if not os.path.isdir(adir):
+            os.mkdir(adir)
+        cmd = '"%s" "%s" -i -a "--directory=%s" "--omit=%s"' % (
+            sys.executable, os.path.join(TESTDIR, 'coverage.py'),
+            adir, omit)
+        vlog("# Running: "+cmd)
+        os.system(cmd)
+
+class Timeout(Exception):
+    pass
+
+def alarmed(signum, frame):
+    raise Timeout
+
+def run(cmd, options):
+    """Run command in a sub-process, capturing the output (stdout and stderr).
+    Return the exist code, and output."""
+    # TODO: Use subprocess.Popen if we're running on Python 2.4
+    if os.name == 'nt' or sys.platform.startswith('java'):
+        tochild, fromchild = os.popen4(cmd)
+        tochild.close()
+        output = fromchild.read()
+        ret = fromchild.close()
+        if ret == None:
+            ret = 0
+    else:
+        proc = Popen4(cmd)
+        try:
+            output = ''
+            proc.tochild.close()
+            output = proc.fromchild.read()
+            ret = proc.wait()
+            if os.WIFEXITED(ret):
+                ret = os.WEXITSTATUS(ret)
+        except Timeout:
+            vlog('# Process %d timed out - killing it' % proc.pid)
+            os.kill(proc.pid, signal.SIGTERM)
+            ret = proc.wait()
+            if ret == 0:
+                ret = signal.SIGTERM << 8
+            output += ("\n### Abort: timeout after %d seconds.\n"
+                       % options.timeout)
+    return ret, splitnewlines(output)
+
+def runone(options, test, skips, fails):
+    '''tristate output:
+    None -> skipped
+    True -> passed
+    False -> failed'''
+
+    def skip(msg):
+        if not options.verbose:
+            skips.append((test, msg))
+        else:
+            print "\nSkipping %s: %s" % (test, msg)
+        return None
+
+    def fail(msg):
+        fails.append((test, msg))
+        if not options.nodiff:
+            print "\nERROR: %s %s" % (test, msg)
+        return None
+
+    vlog("# Test", test)
+
+    # create a fresh hgrc
+    hgrc = file(HGRCPATH, 'w+')
+    hgrc.write('[ui]\n')
+    hgrc.write('slash = True\n')
+    hgrc.write('[defaults]\n')
+    hgrc.write('backout = -d "0 0"\n')
+    hgrc.write('commit = -d "0 0"\n')
+    hgrc.write('debugrawcommit = -d "0 0"\n')
+    hgrc.write('tag = -d "0 0"\n')
+    hgrc.close()
+
+    err = os.path.join(TESTDIR, test+".err")
+    ref = os.path.join(TESTDIR, test+".out")
+    testpath = os.path.join(TESTDIR, test)
+
+    if os.path.exists(err):
+        os.remove(err)       # Remove any previous output files
+
+    # Make a tmp subdirectory to work in
+    tmpd = os.path.join(HGTMP, test)
+    os.mkdir(tmpd)
+    os.chdir(tmpd)
+
+    try:
+        tf = open(testpath)
+        firstline = tf.readline().rstrip()
+        tf.close()
+    except:
+        firstline = ''
+    lctest = test.lower()
+
+    if lctest.endswith('.py') or firstline == '#!/usr/bin/env python':
+        cmd = '%s "%s"' % (PYTHON, testpath)
+    elif lctest.endswith('.bat'):
+        # do not run batch scripts on non-windows
+        if os.name != 'nt':
+            return skip("batch script")
+        # To reliably get the error code from batch files on WinXP,
+        # the "cmd /c call" prefix is needed. Grrr
+        cmd = 'cmd /c call "%s"' % testpath
+    else:
+        # do not run shell scripts on windows
+        if os.name == 'nt':
+            return skip("shell script")
+        # do not try to run non-executable programs
+        if not os.path.exists(testpath):
+            return fail("does not exist")
+        elif not os.access(testpath, os.X_OK):
+            return skip("not executable")
+        cmd = '"%s"' % testpath
+
+    if options.timeout > 0:
+        signal.alarm(options.timeout)
+
+    vlog("# Running", cmd)
+    ret, out = run(cmd, options)
+    vlog("# Ret was:", ret)
+
+    if options.timeout > 0:
+        signal.alarm(0)
+
+    mark = '.'
+
+    skipped = (ret == SKIPPED_STATUS)
+    # If reference output file exists, check test output against it
+    if os.path.exists(ref):
+        f = open(ref, "r")
+        refout = splitnewlines(f.read())
+        f.close()
+    else:
+        refout = []
+    if skipped:
+        mark = 's'
+        missing, failed = parsehghaveoutput(out)
+        if not missing:
+            missing = ['irrelevant']
+        if failed:
+            fail("hghave failed checking for %s" % failed[-1])
+            skipped = False
+        else:
+            skip(missing[-1])
+    elif out != refout:
+        mark = '!'
+        if ret:
+            fail("output changed and returned error code %d" % ret)
+        else:
+            fail("output changed")
+        if not options.nodiff:
+            showdiff(refout, out)
+        ret = 1
+    elif ret:
+        mark = '!'
+        fail("returned error code %d" % ret)
+
+    if not options.verbose:
+        sys.stdout.write(mark)
+        sys.stdout.flush()
+
+    if ret != 0 and not skipped:
+        # Save errors to a file for diagnosis
+        f = open(err, "wb")
+        for line in out:
+            f.write(line)
+        f.close()
+
+    # Kill off any leftover daemon processes
+    try:
+        fp = file(DAEMON_PIDS)
+        for line in fp:
+            try:
+                pid = int(line)
+            except ValueError:
+                continue
+            try:
+                os.kill(pid, 0)
+                vlog('# Killing daemon process %d' % pid)
+                os.kill(pid, signal.SIGTERM)
+                time.sleep(0.25)
+                os.kill(pid, 0)
+                vlog('# Daemon process %d is stuck - really killing it' % pid)
+                os.kill(pid, signal.SIGKILL)
+            except OSError, err:
+                if err.errno != errno.ESRCH:
+                    raise
+        fp.close()
+        os.unlink(DAEMON_PIDS)
+    except IOError:
+        pass
+
+    os.chdir(TESTDIR)
+    if not options.keep_tmpdir:
+        shutil.rmtree(tmpd, True)
+    if skipped:
+        return None
+    return ret == 0
+
+def runchildren(options, expecthg, tests):
+    if not options.with_hg:
+        installhg(options)
+        if hgpkg != expecthg:
+            print '# Testing unexpected mercurial: %s' % hgpkg
+
+    optcopy = dict(options.__dict__)
+    optcopy['jobs'] = 1
+    optcopy['with_hg'] = INST
+    opts = []
+    for opt, value in optcopy.iteritems():
+        name = '--' + opt.replace('_', '-')
+        if value is True:
+            opts.append(name)
+        elif value is not None:
+            opts.append(name + '=' + str(value))
+
+    tests.reverse()
+    jobs = [[] for j in xrange(options.jobs)]
+    while tests:
+        for job in jobs:
+            if not tests: break
+            job.append(tests.pop())
+    fps = {}
+    for j, job in enumerate(jobs):
+        if not job:
+            continue
+        rfd, wfd = os.pipe()
+        childopts = ['--child=%d' % wfd, '--port=%d' % (options.port + j * 3)]
+        cmdline = [PYTHON, sys.argv[0]] + opts + childopts + job
+        vlog(' '.join(cmdline))
+        fps[os.spawnvp(os.P_NOWAIT, cmdline[0], cmdline)] = os.fdopen(rfd, 'r')
+        os.close(wfd)
+    failures = 0
+    tested, skipped, failed = 0, 0, 0
+    skips = []
+    fails = []
+    while fps:
+        pid, status = os.wait()
+        fp = fps.pop(pid)
+        l = fp.read().splitlines()
+        test, skip, fail = map(int, l[:3])
+        split = -fail or len(l)
+        for s in l[3:split]:
+            skips.append(s.split(" ", 1))
+        for s in l[split:]:
+            fails.append(s.split(" ", 1))
+        tested += test
+        skipped += skip
+        failed += fail
+        vlog('pid %d exited, status %d' % (pid, status))
+        failures |= status
+    print
+    for s in skips:
+        print "Skipped %s: %s" % (s[0], s[1])
+    for s in fails:
+        print "Failed %s: %s" % (s[0], s[1])
+
+    if hgpkg != expecthg:
+        print '# Tested unexpected mercurial: %s' % hgpkg
+    print "# Ran %d tests, %d skipped, %d failed." % (
+        tested, skipped, failed)
+    sys.exit(failures != 0)
+
+def runtests(options, expecthg, tests):
+    global DAEMON_PIDS, HGRCPATH
+    DAEMON_PIDS = os.environ["DAEMON_PIDS"] = os.path.join(HGTMP, 'daemon.pids')
+    HGRCPATH = os.environ["HGRCPATH"] = os.path.join(HGTMP, '.hgrc')
+
+    try:
+        if not options.with_hg:
+            installhg(options)
+
+            if hgpkg != expecthg:
+                print '# Testing unexpected mercurial: %s' % hgpkg
+
+        if options.timeout > 0:
+            try:
+                signal.signal(signal.SIGALRM, alarmed)
+                vlog('# Running tests with %d-second timeout' %
+                     options.timeout)
+            except AttributeError:
+                print 'WARNING: cannot run tests with timeouts'
+                options.timeout = 0
+
+        tested = 0
+        failed = 0
+        skipped = 0
+
+        if options.restart:
+            orig = list(tests)
+            while tests:
+                if os.path.exists(tests[0] + ".err"):
+                    break
+                tests.pop(0)
+            if not tests:
+                print "running all tests"
+                tests = orig
+
+        skips = []
+        fails = []
+        for test in tests:
+            if options.retest and not os.path.exists(test + ".err"):
+                skipped += 1
+                continue
+            ret = runone(options, test, skips, fails)
+            if ret is None:
+                skipped += 1
+            elif not ret:
+                if options.interactive:
+                    print "Accept this change? [n] ",
+                    answer = sys.stdin.readline().strip()
+                    if answer.lower() in "y yes".split():
+                        rename(test + ".err", test + ".out")
+                        tested += 1
+                        fails.pop()
+                        continue
+                failed += 1
+                if options.first:
+                    break
+            tested += 1
+
+        if options.child:
+            fp = os.fdopen(options.child, 'w')
+            fp.write('%d\n%d\n%d\n' % (tested, skipped, failed))
+            for s in skips:
+                fp.write("%s %s\n" % s)
+            for s in fails:
+                fp.write("%s %s\n" % s)
+            fp.close()
+        else:
+            print
+            for s in skips:
+                print "Skipped %s: %s" % s
+            for s in fails:
+                print "Failed %s: %s" % s
+            if hgpkg != expecthg:
+                print '# Tested unexpected mercurial: %s' % hgpkg
+            print "# Ran %d tests, %d skipped, %d failed." % (
+                tested, skipped, failed)
+
+        if options.anycoverage:
+            outputcoverage(options)
+    except KeyboardInterrupt:
+        failed = True
+        print "\ninterrupted!"
+
+    if failed:
+        sys.exit(1)
+
+hgpkg = None
+def main():
+    (options, args) = parseargs()
+    if not options.child:
+        os.umask(022)
+
+        checktools()
+
+    # Reset some environment variables to well-known values so that
+    # the tests produce repeatable output.
+    os.environ['LANG'] = os.environ['LC_ALL'] = 'C'
+    os.environ['TZ'] = 'GMT'
+    os.environ["EMAIL"] = "Foo Bar <foo.bar@example.com>"
+    os.environ['CDPATH'] = ''
+
+    global TESTDIR, HGTMP, INST, BINDIR, PYTHONDIR, COVERAGE_FILE
+    TESTDIR = os.environ["TESTDIR"] = os.getcwd()
+    HGTMP = os.environ['HGTMP'] = os.path.realpath(tempfile.mkdtemp('', 'hgtests.',
+                                                   options.tmpdir))
+    DAEMON_PIDS = None
+    HGRCPATH = None
+
+    os.environ["HGEDITOR"] = sys.executable + ' -c "import sys; sys.exit(0)"'
+    os.environ["HGMERGE"] = "internal:merge"
+    os.environ["HGUSER"]   = "test"
+    os.environ["HGENCODING"] = "ascii"
+    os.environ["HGENCODINGMODE"] = "strict"
+    os.environ["HGPORT"] = str(options.port)
+    os.environ["HGPORT1"] = str(options.port + 1)
+    os.environ["HGPORT2"] = str(options.port + 2)
+
+    if options.with_hg:
+        INST = options.with_hg
+    else:
+        INST = os.path.join(HGTMP, "install")
+    BINDIR = os.environ["BINDIR"] = os.path.join(INST, "bin")
+    PYTHONDIR = os.path.join(INST, "lib", "python")
+    COVERAGE_FILE = os.path.join(TESTDIR, ".coverage")
+
+    expecthg = os.path.join(HGTMP, 'install', 'lib', 'python', 'mercurial')
+
+    if len(args) == 0:
+        args = os.listdir(".")
+        args.sort()
+
+    tests = []
+    for test in args:
+        if (test.startswith("test-") and '~' not in test and
+            ('.' not in test or test.endswith('.py') or
+             test.endswith('.bat'))):
+            tests.append(test)
+
+    vlog("# Using TESTDIR", TESTDIR)
+    vlog("# Using HGTMP", HGTMP)
+
+    try:
+        if len(tests) > 1 and options.jobs > 1:
+            runchildren(options, expecthg, tests)
+        else:
+            runtests(options, expecthg, tests)
+    finally:
+        cleanup(options)
+
+main()

File LocalMercurial/hgext/histedit/tests/test-histedit-commute

View file
+#!/bin/sh
+
+cat >> $HGRCPATH <<EOF
+[extensions]
+hgext.graphlog=
+EOF
+
+echo "histedit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
+
+
+EDITED=`pwd`/editedhistory
+cat > $EDITED <<EOF
+pick 177f92b77385 c
+pick e860deea161a e
+pick 652413bf663e f
+pick 055a42cdd887 d
+EOF
+initrepo ()
+{
+    hg init r
+    cd r
+    for x in a b c d e f ; do
+        echo $x > $x
+        hg add $x
+        hg ci -m $x
+    done
+}
+
+initrepo
+echo % help text
+hg help histedit
+
+echo % log before edit
+hg log --graph
+
+echo % show the edit commands offered
+HGEDITOR=cat hg histedit 177f92b77385
+
+echo % edit the history
+HGEDITOR="cat $EDITED > " hg histedit 177f92b77385 2>&1 | grep -v 'saving bundle'
+
+echo % log after edit
+hg log --graph
+
+echo % put things back
+
+cat > $EDITED <<EOF
+pick 177f92b77385 c
+pick 853c68da763f d
+pick b069cc29fb22 e
+pick 26f6a030ae82 f
+EOF
+HGEDITOR="cat $EDITED > " hg histedit 177f92b77385 2>&1 | grep -v 'saving bundle'
+
+hg log --graph
+
+
+echo % slightly different this time
+
+cat > $EDITED <<EOF
+pick 055a42cdd887 d
+pick 652413bf663e f
+pick e860deea161a e
+pick 177f92b77385 c
+EOF
+HGEDITOR="cat $EDITED > " hg histedit 177f92b77385 2>&1 | grep -v 'saving bundle'
+hg log --graph
+
+
+echo % keep prevents stripping dead revs
+cat > $EDITED <<EOF
+pick bfe4a5a76b37 d
+pick c4f52e213402 f
+pick 99a62755c625 c
+pick 7c6fdd608667 e
+EOF
+HGEDITOR="cat $EDITED > " hg histedit bfe4a5a76b37 --keep 2>&1 | grep -v 'saving bundle'
+hg log --graph

File LocalMercurial/hgext/histedit/tests/test-histedit-commute.out

View file
+% help text
+hg histedit Interactive history editing.
+
+Inspired by git rebase --interactive.
+
+
+hg histedit <parent>
+
+options:
+
+ -c --continue  continue an edit already in progress
+ -k --keep      strip old nodes after edit is complete
+    --abort     abort an edit in progress
+ -o --outgoing  changesets not found in destination
+ -f --force     force outgoing even for unrelated repositories
+
+use "hg -v help histedit" to show global options
+% log before edit
+@  changeset:   5:652413bf663e
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   4:e860deea161a
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   3:055a42cdd887
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   2:177f92b77385
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% show the edit commands offered
+pick 177f92b77385 c
+pick 055a42cdd887 d
+pick e860deea161a e
+pick 652413bf663e f
+
+# Edit history between 313737663932 and 652413bf663e
+#
+# 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
+#
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+% edit the history
+0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 3 changes to 3 files
+% log after edit
+@  changeset:   5:853c68da763f
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   4:26f6a030ae82
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   3:b069cc29fb22
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   2:177f92b77385
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% put things back
+0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 3 changes to 3 files
+@  changeset:   5:652413bf663e
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   4:e860deea161a
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   3:055a42cdd887
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   2:177f92b77385
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% slightly different this time
+0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 4 changesets with 4 changes to 4 files
+@  changeset:   5:99a62755c625
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   4:7c6fdd608667
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   3:c4f52e213402
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   2:bfe4a5a76b37
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% keep prevents stripping dead revs
+0 files updated, 0 files merged, 2 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+@  changeset:   7:99e266581538
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   6:5ad36efb0653
+|  parent:      3:c4f52e213402
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+| o  changeset:   5:99a62755c625
+| |  user:        test
+| |  date:        Thu Jan 01 00:00:00 1970 +0000
+| |  summary:     c
+| |
+| o  changeset:   4:7c6fdd608667
+|/   user:        test
+|    date:        Thu Jan 01 00:00:00 1970 +0000
+|    summary:     e
+|
+o  changeset:   3:c4f52e213402
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   2:bfe4a5a76b37
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+

File LocalMercurial/hgext/histedit/tests/test-histedit-drop

View file
+#!/bin/sh
+
+cat >> $HGRCPATH <<EOF
+[extensions]
+hgext.graphlog=
+EOF
+echo "histedit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH
+
+EDITED=`pwd`/editedhistory
+cat > $EDITED <<EOF
+drop 177f92b77385 c
+pick e860deea161a e
+pick 652413bf663e f
+pick 055a42cdd887 d
+EOF
+initrepo ()
+{
+    hg init r
+    cd r
+    for x in a b c d e f ; do
+        echo $x > $x
+        hg add $x
+        hg ci -m $x
+    done
+}
+
+initrepo
+
+echo % log before edit
+hg log --graph
+
+echo % edit the history
+HGEDITOR="cat $EDITED > " hg histedit 177f92b77385 2>&1 | grep -v 'saving bundle'
+
+echo % log after edit
+hg log --graph
+
+echo % manifest after edit
+hg manifest
+
+echo % EOF

File LocalMercurial/hgext/histedit/tests/test-histedit-drop.out

View file
+% log before edit
+@  changeset:   5:652413bf663e
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   4:e860deea161a
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   3:055a42cdd887
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   2:177f92b77385
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     c
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% edit the history
+0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+0 files updated, 0 files merged, 0 files removed, 0 files unresolved
+adding branch
+adding changesets
+adding manifests
+adding file changes
+added 3 changesets with 3 changes to 3 files
+% log after edit
+@  changeset:   4:708943196e52
+|  tag:         tip
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     d
+|
+o  changeset:   3:75cbdffecadb
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     f
+|
+o  changeset:   2:493dc0964412
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     e
+|
+o  changeset:   1:d2ae7f538514
+|  user:        test
+|  date:        Thu Jan 01 00:00:00 1970 +0000
+|  summary:     b
+|
+o  changeset:   0:cb9a9f314b8b
+   user:        test
+   date:        Thu Jan 01 00:00:00 1970 +0000
+   summary:     a
+
+% manifest after edit
+a
+b
+d
+e
+f
+% EOF

File LocalMercurial/hgext/histedit/tests/test-histedit-edit

View file
+#!/bin/sh
+
+cat >> $HGRCPATH <<EOF
+[extensions]
+hgext.graphlog=
+EOF
+
+echo "histedit=$(echo $(dirname $(dirname $0)))" >> $HGRCPATH