changeset 1331:ffa32ca659f1

merge with stable
author Pierre-Yves David <pierre-yves.david@fb.com>
date Wed, 01 Apr 2015 16:47:22 -0700
parents c15d4677f2ba (diff) 5b6c42a3e12f (current diff)
children 10ad8e575315
files hgext/evolve.py
diffstat 11 files changed, 911 insertions(+), 70 deletions(-) [+]
line wrap: on
line diff
--- a/hgext/evolve.py
+++ b/hgext/evolve.py
@@ -44,6 +44,8 @@
 except (ImportError, AttributeError):
     gboptslist = gboptsmap = None
 
+# Flags for enabling optional parts of evolve
+commandopt = 'allnewcommands'
 
 from mercurial import base85
 from mercurial import bookmarks
@@ -141,8 +143,11 @@
         """
         for cont, funcname, func in self._duckpunchers:
             setattr(cont, funcname, func)
-        for command, wrapper in self._commandwrappers:
-            extensions.wrapcommand(commands.table, command, wrapper)
+        for command, wrapper, opts in self._commandwrappers:
+            entry = extensions.wrapcommand(commands.table, command, wrapper)
+            if opts:
+                for short, long, val, msg in opts:
+                    entry[1].append((short, long, val, msg))
         for cont, funcname, wrapper in self._functionwrappers:
             extensions.wrapfunction(cont, funcname, wrapper)
         for c in self._uicallables:
@@ -163,13 +168,20 @@
             revset.symbols[name] = symbol
         for name, kw in self._templatekws:
             templatekw.keywords[name] = kw
-        for ext, command, wrapper in self._extcommandwrappers:
+        for ext, command, wrapper, opts in self._extcommandwrappers:
             if ext not in knownexts:
-                e = extensions.find(ext)
-                if e is None:
-                    raise util.Abort('extension %s not found' % ext)
+                try:
+                    e = extensions.find(ext)
+                except KeyError:
+                    # Extension isn't enabled, so don't bother trying to wrap
+                    # it.
+                    continue
                 knownexts[ext] = e.cmdtable
-            extensions.wrapcommand(knownexts[ext], commands, wrapper)
+            entry = extensions.wrapcommand(knownexts[ext], command, wrapper)
+            if opts:
+                for short, long, val, msg in opts:
+                    entry[1].append((short, long, val, msg))
+
         for c in self._extcallables:
             c(ui)
 
@@ -257,7 +269,7 @@
             return keyword
         return dec
 
-    def wrapcommand(self, command, extension=None):
+    def wrapcommand(self, command, extension=None, opts=[]):
         """Decorated function is a command wrapper
 
         The name of the command must be given as the decorator argument.
@@ -276,12 +288,16 @@
                 ui.note('Barry!')
                 return orig(ui, repo, *args, **kwargs)
 
+        The `opts` argument allows specifying additional arguments for the
+        command.
+
         """
         def dec(wrapper):
             if extension is None:
-                self._commandwrappers.append((command, wrapper))
+                self._commandwrappers.append((command, wrapper, opts))
             else:
-                self._extcommandwrappers.append((extension, command, wrapper))
+                self._extcommandwrappers.append((extension, command, wrapper,
+                    opts))
             return wrapper
         return dec
 
@@ -327,6 +343,31 @@
 reposetup = eh.final_reposetup
 
 #####################################################################
+### Option configuration                                          ###
+#####################################################################
+
+@eh.reposetup # must be the first of its kin.
+def _configureoptions(ui, repo):
+    # If no capabilities are specified, enable everything.
+    # This is so existing evolve users don't need to change their config.
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = ['all']
+        ui.setconfig('experimental', 'evolution', evolveopts)
+
+@eh.uisetup
+def _configurecmdoptions(ui):
+    # Unregister evolve commands if the command capability is not specified.
+    #
+    # This must be in the same function as the option configuration above to
+    # guarantee it happens after the above configuration, but before the
+    # extsetup functions.
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if evolveopts and (commandopt not in evolveopts and
+                       'all' not in evolveopts):
+        cmdtable.clear()
+
+#####################################################################
 ### experimental behavior                                         ###
 #####################################################################
 
@@ -931,7 +972,11 @@
 
     This function is loosely based on the extensions.wrapcommand function.
     '''
-    aliases, entry = cmdutil.findcmd(newalias, cmdtable)
+    try:
+        aliases, entry = cmdutil.findcmd(newalias, cmdtable)
+    except error.UnknownCommand:
+        # Commands may be disabled
+        return
     for alias, e in cmdtable.iteritems():
         if e is entry:
             break
@@ -1422,6 +1467,9 @@
         files = set()
         copied = copies.pathcopies(prec, bumped)
         precmanifest = prec.manifest()
+        # 3.3.2 needs a list.
+        # future 3.4 don't detect the size change during iteration
+        # this is fishy
         for key, val in list(bumped.manifest().iteritems()):
             precvalue = precmanifest.get(key, None)
             if precvalue is not None:
@@ -1710,6 +1758,7 @@
     [('n', 'new', [], _("successor changeset (DEPRECATED)")),
      ('s', 'succ', [], _("successor changeset")),
      ('r', 'rev', [], _("revisions to prune")),
+     ('k', 'keep', None, _("does not modify working copy during prune")),
      ('', 'biject', False, _("do a 1-1 map between rev and successor ranges")),
      ('B', 'bookmark', '', _("remove revs only reachable from given"
                              " bookmark"))] + metadataopts,
@@ -1802,8 +1851,28 @@
                 newnode = newnode.parents()[0]
 
         if newnode.node() != wdp.node():
-            commands.update(ui, repo, newnode.rev())
-            ui.status(_('working directory now at %s\n') % newnode)
+            if opts.get('keep', False):
+                # This is largely the same as the implementation in
+                # strip.stripcmd(). We might want to refactor this somewhere
+                # common at some point.
+
+                # only reset the dirstate for files that would actually change
+                # between the working context and uctx
+                descendantrevs = repo.revs("%d::." % newnode.rev())
+                changedfiles = []
+                for rev in descendantrevs:
+                    # blindly reset the files, regardless of what actually changed
+                    changedfiles.extend(repo[rev].files())
+
+                # reset files that only changed in the dirstate too
+                dirstate = repo.dirstate
+                dirchanges = [f for f in dirstate if dirstate[f] != 'n']
+                changedfiles.extend(dirchanges)
+                repo.dirstate.rebuild(newnode.node(), newnode.manifest(), changedfiles)
+                repo.dirstate.write()
+            else:
+                commands.update(ui, repo, newnode.rev())
+                ui.status(_('working directory now at %s\n') % newnode)
         # update bookmarks
         if bookmark:
             _deletebookmark(ui, marks, bookmark)
@@ -2065,6 +2134,31 @@
         if lock is not None:
             lock.release()
 
+@eh.wrapcommand('strip', extension='strip', opts=[
+    ('', 'bundle', None, _("delete the commit entirely and move it to a "
+        "backup bundle")),
+    ])
+def stripwrapper(orig, ui, repo, *revs, **kwargs):
+    if (not ui.configbool('experimental', 'prunestrip') or
+        kwargs.get('bundle', False)):
+        return orig(ui, repo, *revs, **kwargs)
+
+    if kwargs.get('force'):
+        ui.warn(_("warning: --force has no effect during strip with evolve "
+                  "enabled\n"))
+    if kwargs.get('no_backup', False):
+        ui.warn(_("warning: --no-backup has no effect during strips with "
+                  "evolve enabled\n"))
+
+    revs = list(revs) + kwargs.pop('rev', [])
+    revs = set(scmutil.revrange(repo, revs))
+    revs = repo.revs("(%ld)::", revs)
+    kwargs['rev'] = []
+    kwargs['new'] = []
+    kwargs['succ'] = []
+    kwargs['biject'] = False
+    return cmdprune(ui, repo, *revs, **kwargs)
+
 @command('^touch',
     [('r', 'rev', [], 'revision to update'),
      ('D', 'duplicate', False,
@@ -2258,8 +2352,12 @@
 @eh.extsetup
 def oldevolveextsetup(ui):
     for cmd in ['kill', 'uncommit', 'touch', 'fold']:
-        entry = extensions.wrapcommand(cmdtable, cmd,
-                                       warnobserrors)
+        try:
+            entry = extensions.wrapcommand(cmdtable, cmd,
+                                           warnobserrors)
+        except error.UnknownCommand:
+            # Commands may be disabled
+            continue
 
     entry = cmdutil.findcmd('commit', commands.table)[1]
     entry[1].append(('o', 'obsolete', [],
@@ -2291,7 +2389,7 @@
 if getattr(exchange, '_pushdiscoveryobsmarkers', None) is not None:
     @eh.wrapfunction(exchange, '_pushdiscoveryobsmarkers')
     def _pushdiscoveryobsmarkers(orig, pushop):
-        if (obsolete._enabled
+        if (obsolete.isenabled(pushop.repo, obsolete.exchangeopt)
             and pushop.repo.obsstore
             and 'obsolete' in pushop.remote.listkeys('namespaces')):
             repo = pushop.repo
@@ -2333,7 +2431,7 @@
 def discocapabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _evoext_obshash_0'
     return caps
 
@@ -2468,7 +2566,7 @@
     remote = pushop.remote
     unfi = repo.unfiltered()
     cl = unfi.changelog
-    if (obsolete._enabled and repo.obsstore and
+    if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore and
         'obsolete' in remote.listkeys('namespaces')):
         markers = pushop.outobsmarkers
         if not markers:
@@ -2632,7 +2730,7 @@
 
 @eh.wrapfunction(exchange, '_pullobsolete')
 def _pullobsolete(orig, pullop):
-    if not obsolete._enabled:
+    if not obsolete.isenabled(pullop.repo, obsolete.exchangeopt):
         return None
     if 'obsmarkers' not in getattr(pullop, 'todosteps', ['obsmarkers']):
         return None
@@ -2836,7 +2934,7 @@
 def capabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _evoext_pushobsmarkers_0'
         caps += ' _evoext_pullobsmarkers_0'
         caps += ' _evoext_obshash_0'
new file mode 100644
--- /dev/null
+++ b/hgext/inhibit.py
@@ -0,0 +1,221 @@
+"""Reduce the changesets evolution feature scope for early and noob friendly UI
+
+The full scale changeset evolution have some massive bleeding edge and it is
+very easy for people not very intimate with the concept to end up in intricate
+situation. In order to get some of the benefit sooner, this extension is
+disabling some of the less polished aspect of evolution. It should gradually
+get thinner and thinner as changeset evolution will get more polished. This
+extension is only recommended for large scale organisations. Individual user
+should probably stick on using Evolution in its current state, understand its
+concept and provide feedback
+
+The first feature provided by this extension is the ability to "inhibit"
+obsolescence markers. Obsolete revision can be cheaply brought back to life
+that way. However as the inhibitor are not fitting in an append only model,
+this is incompatible with sharing mutable history.
+
+The second feature is called direct access. It is the ability to refer and
+access hidden sha in commands provided that you know their value.
+For example hg log -r XXX where XXX is a commit has should work whether XXX is
+hidden or not as we assume that the user knows what he is doing when referring
+to XXX.
+"""
+from mercurial import localrepo
+from mercurial import obsolete
+from mercurial import extensions
+from mercurial import cmdutil
+from mercurial import scmutil
+from mercurial import repoview
+from mercurial import revset
+from mercurial import error
+from mercurial import commands
+from mercurial import bookmarks
+
+cmdtable = {}
+command = cmdutil.command(cmdtable)
+
+def reposetup(ui, repo):
+
+    class obsinhibitedrepo(repo.__class__):
+
+        @localrepo.storecache('obsinhibit')
+        def _obsinhibit(self):
+            # XXX we should make sure it is invalidated by transaction failure
+            obsinhibit = set()
+            raw = self.sopener.tryread('obsinhibit')
+            for i in xrange(0, len(raw), 20):
+                obsinhibit.add(raw[i:i+20])
+            return obsinhibit
+
+    repo.__class__ = obsinhibitedrepo
+    repo._explicitaccess = set()
+
+
+def _update(orig, ui, repo, *args, **kwargs):
+    """
+    When moving to a commit we want to inhibit any obsolete commit affecting
+    the changeset we are updating to. In other words we don't want any visible
+    commit to be obsolete.
+    """
+    res = orig(ui, repo, *args, **kwargs)
+    newhead = repo['.'].node()
+    _inhibitmarkers(repo, [newhead])
+    return res
+
+def _bookmarkchanged(orig, bkmstoreinst, *args, **kwargs):
+    """ Add inhibition markers to every obsolete bookmarks """
+    repo = bkmstoreinst._repo
+    bkmstorenodes = [repo[v].node() for v in bkmstoreinst.values()]
+    _inhibitmarkers(repo, bkmstorenodes)
+    return orig(bkmstoreinst, *args, **kwargs)
+
+# obsolescence inhibitor
+########################
+
+def _schedulewrite(tr, obsinhibit):
+    """Make sure on disk content will be updated on transaction commit"""
+    def writer(fp):
+        """Serialize the inhibited list to disk.
+        """
+        raw = ''.join(obsinhibit)
+        fp.write(raw)
+    tr.addfilegenerator('obsinhibit', ('obsinhibit',), writer)
+    tr.hookargs['obs_inbihited'] = '1'
+
+def _filterpublic(repo, nodes):
+    """filter out inhibitor on public changeset
+
+    Public changesets are already immune to obsolescence"""
+    getrev = repo.changelog.nodemap.get
+    getphase = repo._phasecache.phase
+    return (n for n in repo._obsinhibit if getphase(repo, getrev(n)))
+
+def _inhibitmarkers(repo, nodes):
+    """add marker inhibitor for all obsolete revision under <nodes>
+
+    Content of <nodes> and all mutable ancestors are considered. Marker for
+    obsolete revision only are created.
+    """
+    newinhibit = repo.set('::%ln and obsolete()', nodes)
+    if newinhibit:
+        tr = repo.transaction('obsinhibit')
+        try:
+            repo._obsinhibit.update(c.node() for c in newinhibit)
+            _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit))
+            repo.invalidatevolatilesets()
+            tr.close()
+        finally:
+            tr.release()
+
+def _deinhibitmarkers(repo, nodes):
+    """lift obsolescence inhibition on a set of nodes
+
+    This will be triggered when inhibited nodes received new obsolescence
+    markers. Otherwise the new obsolescence markers would also be inhibited.
+    """
+    deinhibited = repo._obsinhibit & set(nodes)
+    if deinhibited:
+        tr = repo.transaction('obsinhibit')
+        try:
+            repo._obsinhibit -= deinhibited
+            _schedulewrite(tr, _filterpublic(repo, repo._obsinhibit))
+            repo.invalidatevolatilesets()
+            tr.close()
+        finally:
+            tr.release()
+
+def _createmarkers(orig, repo, relations, flag=0, date=None, metadata=None):
+    """wrap markers create to make sure we de-inhibit target nodes"""
+    # wrapping transactio to unify the one in each function
+    tr = repo.transaction('add-obsolescence-marker')
+    try:
+        orig(repo, relations, flag, date, metadata)
+        precs = (r[0].node() for r in relations)
+        _deinhibitmarkers(repo, precs)
+        tr.close()
+    finally:
+        tr.release()
+
+def extsetup(ui):
+    # lets wrap the computation of the obsolete set
+    # We apply inhibition there
+    obsfunc = obsolete.cachefuncs['obsolete']
+    def _computeobsoleteset(repo):
+        """remove any inhibited nodes from the obsolete set
+
+        This will trickle down to other part of mercurial (hidden, log, etc)"""
+        obs = obsfunc(repo)
+        getrev = repo.changelog.nodemap.get
+        for n in repo._obsinhibit:
+            obs.discard(getrev(n))
+        return obs
+    obsolete.cachefuncs['obsolete'] = _computeobsoleteset
+    # drop divergence computation since it is incompatible with "light revive"
+    obsolete.cachefuncs['divergent'] = lambda repo: set()
+    # drop bumped computation since it is incompatible with "light revive"
+    obsolete.cachefuncs['bumped'] = lambda repo: set()
+    # wrap create marker to make it able to lift the inhibition
+    extensions.wrapfunction(obsolete, 'createmarkers', _createmarkers)
+    extensions.wrapfunction(repoview, '_getdynamicblockers', _accessvisible)
+    extensions.wrapfunction(revset, 'posttreebuilthook', _posttreebuilthook)
+    # wrap update to make sure that no obsolete commit is visible after an
+    # update
+    extensions.wrapcommand(commands.table, 'update', _update)
+    # There are two ways to save bookmark changes during a transation, we
+    # wrap both to add inhibition markers.
+    extensions.wrapfunction(bookmarks.bmstore, 'recordchange', _bookmarkchanged)
+    extensions.wrapfunction(bookmarks.bmstore, 'write', _bookmarkchanged)
+
+
+def gethashsymbols(tree):
+    # Returns the list of symbols of the tree that look like hashes
+    # for example for the revset 3::abe3ff it will return ('abe3ff')
+    if not tree:
+        return []
+
+    if len(tree) == 2 and tree[0] == "symbol":
+        try:
+            int(tree[1])
+            return []
+        except ValueError as e:
+            return [tree[1]]
+    elif len(tree) == 3:
+        return gethashsymbols(tree[1]) + gethashsymbols(tree[2])
+    else:
+        return []
+
+def _posttreebuilthook(orig, tree, repo):
+    # This is use to enabled direct hash access
+    # We extract the symbols that look like hashes and add them to the
+    # explicitaccess set
+    orig(tree, repo)
+    if repo and repo.filtername == 'visible':
+        prelength = len(repo._explicitaccess)
+        repo.symbols = gethashsymbols(tree)
+        cl = repo.unfiltered().changelog
+        for node in repo.symbols:
+            try:
+                node = cl._partialmatch(node)
+            except error.LookupError:
+                node = None
+            if node is not None:
+                rev = cl.rev(node)
+                if rev not in repo.changelog:
+                    repo._explicitaccess.add(rev)
+        if prelength != len(repo._explicitaccess):
+            repo.invalidatevolatilesets()
+
+@command('debugobsinhibit', [], '')
+def cmddebugobsinhibit(ui, repo, *revs):
+    """inhibit obsolescence markers effect on a set of revs"""
+    nodes = (repo[r].node() for r in scmutil.revrange(repo, revs))
+    _inhibitmarkers(repo, nodes)
+
+# ensure revision accessed by hash are visible
+###############################################
+
+def _accessvisible(orig, repo):
+    """ensure accessed revs stay visible"""
+    blockers = orig(repo)
+    blockers.update(getattr(repo, '_explicitaccess', ()))
+    return blockers
--- a/hgext/obsolete.py
+++ b/hgext/obsolete.py
@@ -14,8 +14,6 @@
 
 try:
     from mercurial import obsolete
-    if not obsolete._enabled:
-        obsolete._enabled = True
 except ImportError:
     raise util.Abort('Obsolete extension requires Mercurial 2.3 (or later)')
 
@@ -40,6 +38,10 @@
     """
     if not repo.local():
         return
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = 'all'
+        ui.setconfig('experimental', 'evolution', evolveopts)
     for arg in sys.argv:
         if 'debugc' in arg:
             break
--- a/hgext/pushexperiment.py
+++ b/hgext/pushexperiment.py
@@ -49,7 +49,8 @@
 
 def syncpush(orig, repo, remote):
     """wraper for obsolete.syncpush to use the fast way if possible"""
-    if not (obsolete._enabled and repo.obsstore):
+    if not (obsolete.isenabled(repo, obsolete.exchangeopt) and
+            repo.obsstore):
         return
     if remote.capable('_push_experiment_pushobsmarkers_0'):
         return # already pushed before changeset
@@ -75,7 +76,7 @@
     """push wrapped that call the wire protocol command"""
     if not remote.canpush():
         raise util.Abort(_("destination does not support push"))
-    if (obsolete._enabled and repo.obsstore
+    if (obsolete.isenabled(repo, obsolete.exchangeopt) and repo.obsstore
         and remote.capable('_push_experiment_pushobsmarkers_0')):
         # push marker early to limit damage of pushing too early.
         try:
@@ -94,7 +95,7 @@
 def capabilities(orig, repo, proto):
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
-    if obsolete._enabled:
+    if obsolete.isenabled(repo, obsolete.exchangeopt):
         caps += ' _push_experiment_pushobsmarkers_0'
     caps += ' _push_experiment_notifypushend_0'
     return caps
--- a/hgext/simple4server.py
+++ b/hgext/simple4server.py
@@ -12,7 +12,6 @@
 buglink = 'https://bitbucket.org/marmoute/mutable-history/issues'
 
 import mercurial.obsolete
-mercurial.obsolete._enabled = True
 
 import struct
 from mercurial import util
@@ -30,8 +29,6 @@
 gboptslist = gboptsmap = None
 try:
     from mercurial import obsolete
-    if not obsolete._enabled:
-        obsolete._enabled = True
     from mercurial import wireproto
     gboptslist = getattr(wireproto, 'gboptslist', None)
     gboptsmap = getattr(wireproto, 'gboptsmap', None)
@@ -233,7 +230,7 @@
     """wrapper to advertise new capability"""
     caps = orig(repo, proto)
     advertise = repo.ui.configbool('__temporary__', 'advertiseobsolete', True)
-    if obsolete._enabled and advertise:
+    if obsolete.isenabled(repo, obsolete.exchangeopt) and advertise:
         caps += ' _evoext_pushobsmarkers_0'
         caps += ' _evoext_pullobsmarkers_0'
         caps += ' _evoext_obshash_0'
@@ -280,3 +277,8 @@
     extensions.wrapfunction(pushkey, '_nslist', _nslist)
     pushkey._namespaces['namespaces'] = (lambda *x: False, pushkey._nslist)
 
+def reposetup(ui, repo):
+    evolveopts = ui.configlist('experimental', 'evolution')
+    if not evolveopts:
+        evolveopts = 'all'
+        ui.setconfig('experimental', 'evolution', evolveopts)
--- a/tests/killdaemons.py
+++ b/tests/killdaemons.py
@@ -1,25 +1,91 @@
 #!/usr/bin/env python
 
-import os, time, errno, signal
+import os, sys, time, errno, signal
+
+if os.name =='nt':
+    import ctypes
+
+    def _check(ret, expectederr=None):
+        if ret == 0:
+            winerrno = ctypes.GetLastError()
+            if winerrno == expectederr:
+                return True
+            raise ctypes.WinError(winerrno)
 
-# Kill off any leftover daemon processes
-try:
-    fp = open(os.environ['DAEMON_PIDS'])
-    for line in fp:
+    def kill(pid, logfn, tryhard=True):
+        logfn('# Killing daemon process %d' % pid)
+        PROCESS_TERMINATE = 1
+        PROCESS_QUERY_INFORMATION = 0x400
+        SYNCHRONIZE = 0x00100000
+        WAIT_OBJECT_0 = 0
+        WAIT_TIMEOUT = 258
+        handle = ctypes.windll.kernel32.OpenProcess(
+                PROCESS_TERMINATE|SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
+                False, pid)
+        if handle == 0:
+            _check(0, 87) # err 87 when process not found
+            return # process not found, already finished
         try:
-            pid = int(line)
-        except ValueError:
-            continue
+            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+            if r == WAIT_OBJECT_0:
+                pass # terminated, but process handle still available
+            elif r == WAIT_TIMEOUT:
+                _check(ctypes.windll.kernel32.TerminateProcess(handle, -1))
+            else:
+                _check(r)
+
+            # TODO?: forcefully kill when timeout
+            #        and ?shorter waiting time? when tryhard==True
+            r = ctypes.windll.kernel32.WaitForSingleObject(handle, 100)
+                                                       # timeout = 100 ms
+            if r == WAIT_OBJECT_0:
+                pass # process is terminated
+            elif r == WAIT_TIMEOUT:
+                logfn('# Daemon process %d is stuck')
+            else:
+                _check(r) # any error
+        except: #re-raises
+            ctypes.windll.kernel32.CloseHandle(handle) # no _check, keep error
+            raise
+        _check(ctypes.windll.kernel32.CloseHandle(handle))
+
+else:
+    def kill(pid, logfn, tryhard=True):
         try:
             os.kill(pid, 0)
+            logfn('# Killing daemon process %d' % pid)
             os.kill(pid, signal.SIGTERM)
-            for i in range(10):
-                time.sleep(0.05)
+            if tryhard:
+                for i in range(10):
+                    time.sleep(0.05)
+                    os.kill(pid, 0)
+            else:
+                time.sleep(0.1)
                 os.kill(pid, 0)
+            logfn('# 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()
-except IOError:
-    pass
+
+def killdaemons(pidfile, tryhard=True, remove=False, logfn=None):
+    if not logfn:
+        logfn = lambda s: s
+    # Kill off any leftover daemon processes
+    try:
+        fp = open(pidfile)
+        for line in fp:
+            try:
+                pid = int(line)
+            except ValueError:
+                continue
+            kill(pid, logfn, tryhard)
+        fp.close()
+        if remove:
+            os.unlink(pidfile)
+    except IOError:
+        pass
+
+if __name__ == '__main__':
+    path, = sys.argv[1:]
+    killdaemons(path)
new file mode 100644
--- /dev/null
+++ b/tests/test-inhibit.t
@@ -0,0 +1,392 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate = {rev}:{node|short} {desc}\n
+  > [extensions]
+  > rebase=
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+  $ echo "inhibit=$(echo $(dirname $TESTDIR))/hgext/inhibit.py" >> $HGRCPATH
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+  $ hg init inhibit
+  $ cd inhibit
+  $ mkcommit cA
+  $ mkcommit cB
+  $ mkcommit cC
+  $ mkcommit cD
+  $ hg up 'desc(cA)'
+  0 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  $ mkcommit cE
+  created new head
+  $ mkcommit cG
+  $ mkcommit cH
+  $ mkcommit cJ
+  $ hg log -G 
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  3:2db36d8066ff add cD
+  | |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+plain prune
+
+  $ hg prune 1::
+  3 changesets pruned
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg debugobsinhibit --hidden 1::
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  3:2db36d8066ff add cD
+  | |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg prune --hidden 1::
+  3 changesets pruned
+  $ hg log -G
+  @  7:18214586bf78 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+after amend
+
+  $ echo babar > cJ
+  $ hg amend
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg debugobsinhibit --hidden 18214586bf78
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+and no divergence
+
+  $ hg summary
+  parent: 9:55c73a90e4b4 tip
+   add cJ
+  branch: default
+  commit: (clean)
+  update: 1 new changesets, 2 branch heads (merge)
+
+check public revision got cleared
+(when adding the second inhibitor, the first one is removed because it is public)
+
+  $ wc -m .hg/store/obsinhibit
+  20 .hg/store/obsinhibit
+  $ hg prune 7
+  1 changesets pruned
+  $ hg debugobsinhibit --hidden 18214586bf78
+  $ wc -m .hg/store/obsinhibit
+  20 .hg/store/obsinhibit
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg phase --public 7
+  $ hg prune 9
+  1 changesets pruned
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory now at cf5c4f4554ce
+  $ hg debugobsinhibit --hidden 55c73a90e4b4
+  $ wc -m .hg/store/obsinhibit
+  20 .hg/store/obsinhibit
+  $ hg log -G
+  o  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  @  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+Update should inhibit all related unstable commits
+
+  $ hg update 2 --hidden
+  2 files updated, 0 files merged, 3 files removed, 0 files unresolved
+  working directory parent is obsolete!
+  $ hg log -G
+  o  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | @  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+
+  $ hg update 9
+  4 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg prune --hidden 1::
+  3 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+
+Bookmark should inhibit all related unstable commits
+  $ hg bookmark -r 2 book1  --hidden
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  | o  2:7df62a38b9bf add cC
+  | |
+  | o  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg bookmark -d book1
+  $ hg prune --hidden 1::
+  3 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+Test that direct access make changesets visible
+
+  $ hg export 2db36d8066ff 02bcbc3f6e56
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82
+  # Parent  7df62a38b9bf9daf968de235043ba88a8ef43393
+  add cD
+  
+  diff -r 7df62a38b9bf -r 2db36d8066ff cD
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cD	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cD
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 02bcbc3f6e56fb2928efec2c6e24472720bf5511
+  # Parent  54ccbc537fc2d6845a5d61337c1cfb80d1d2815e
+  add cB
+  
+  diff -r 54ccbc537fc2 -r 02bcbc3f6e56 cB
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cB	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cB
+
+But only with hash
+
+  $ hg export 2db36d8066ff::
+  # HG changeset patch
+  # User test
+  # Date 0 0
+  #      Thu Jan 01 00:00:00 1970 +0000
+  # Node ID 2db36d8066ff50e8be3d3e6c2da1ebc0a8381d82
+  # Parent  7df62a38b9bf9daf968de235043ba88a8ef43393
+  add cD
+  
+  diff -r 7df62a38b9bf -r 2db36d8066ff cD
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/cD	Thu Jan 01 00:00:00 1970 +0000
+  @@ -0,0 +1,1 @@
+  +cD
+
+  $ hg export 1 3
+  abort: hidden revision '1'!
+  (use --hidden to access hidden revisions)
+  [255]
+
+
+With severals hidden sha, rebase of one hidden stack onto another one:
+  $ hg update -C 0
+  0 files updated, 0 files merged, 4 files removed, 0 files unresolved
+  $ mkcommit cK
+  created new head
+  $ mkcommit cL
+  $ hg update -C 9
+  4 files updated, 0 files merged, 2 files removed, 0 files unresolved
+  $ hg log -G
+  o  11:53a94305e133 add cL
+  |
+  o  10:ad78ff7d621f add cK
+  |
+  | @  9:55c73a90e4b4 add cJ
+  | |
+  | | o  7:18214586bf78 add cJ
+  | |/
+  | o  6:cf5c4f4554ce add cH
+  | |
+  | o  5:5419eb264a33 add cG
+  | |
+  | o  4:98065434e5c6 add cE
+  |/
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg prune 10:
+  2 changesets pruned
+  $ hg log -G
+  @  9:55c73a90e4b4 add cJ
+  |
+  | o  7:18214586bf78 add cJ
+  |/
+  o  6:cf5c4f4554ce add cH
+  |
+  o  5:5419eb264a33 add cG
+  |
+  o  4:98065434e5c6 add cE
+  |
+  o  0:54ccbc537fc2 add cA
+  
+  $ hg rebase -s 10 -d 3 
+  abort: hidden revision '3'!
+  (use --hidden to access hidden revisions)
+  [255]
+  $ hg rebase -r ad78ff7d621f -r 53a94305e133 -d  2db36d8066ff
+  rebasing 10:ad78ff7d621f "add cK"
+  rebasing 11:53a94305e133 "add cL"
+  2 new unstable changesets
+  $ hg log -G
+  o  13:2f7b7704d714 add cL
+  |
+  o  12:fe1634cbe235 add cK
+  |
+  | @  9:55c73a90e4b4 add cJ
+  | |
+  | | o  7:18214586bf78 add cJ
+  | |/
+  | o  6:cf5c4f4554ce add cH
+  | |
+  | o  5:5419eb264a33 add cG
+  | |
+  | o  4:98065434e5c6 add cE
+  | |
+  x |  3:2db36d8066ff add cD
+  | |
+  x |  2:7df62a38b9bf add cC
+  | |
+  x |  1:02bcbc3f6e56 add cB
+  |/
+  o  0:54ccbc537fc2 add cA
+  
new file mode 100644
--- /dev/null
+++ b/tests/test-options.t
@@ -0,0 +1,30 @@
+  $ cat >> $HGRCPATH <<EOF
+  > [ui]
+  > logtemplate={rev}:{node|short}[{bookmarks}] ({obsolete}/{phase}) {desc|firstline}\n
+  > [extensions]
+  > EOF
+  $ echo "evolve=$(echo $(dirname $TESTDIR))/hgext/evolve.py" >> $HGRCPATH
+
+  $ mkcommit() {
+  >    echo "$1" > "$1"
+  >    hg add "$1"
+  >    hg ci -m "add $1"
+  > }
+
+  $ hg init repo
+  $ cd repo
+  $ mkcommit a
+  $ mkcommit b
+
+test disabling commands
+
+  $ cat >> .hg/hgrc <<EOF
+  > [experimental]
+  > evolution=createmarkers
+  >   allowunstable
+  >   exchange
+  > EOF
+  $ hg prune | head -n 2
+  hg: unknown command 'prune'
+  Mercurial Distributed SCM
+  
--- a/tests/test-prune.t
+++ b/tests/test-prune.t
@@ -223,6 +223,35 @@
   cb7f8f706a6532967b98cf8583a81baab79a0fa7 8ee176ff1d4b2034ce51e3efc579c2de346b631d 0 (*) {'user': 'test'} (glob)
   21b6f2f1cece8c10326e575dd38239189d467190 6e8148413dd541855b72a920a90c06fca127c7e7 0 (*) {'user': 'test'} (glob)
 
+test hg strip replacement
+
+  $ hg up 10
+  2 files updated, 0 files merged, 0 files removed, 0 files unresolved
+  $ mkcommit n1
+  created new head
+  $ mkcommit n2
+  $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r .
+  1 changesets pruned
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  working directory now at c7e58696a948
+  $ hg --config extensions.strip= --config experimental.prunestrip=True strip -r . --bundle
+  0 files updated, 0 files merged, 1 files removed, 0 files unresolved
+  saved backup bundle to $TESTTMP/repo/.hg/strip-backup/c7e58696a948-69ca36d3-backup.hg (glob)
+
+test hg prune --keep
+  $ mkcommit n1
+  created new head
+  $ hg diff -r .^
+  diff -r aa96dc3f04c2 n1
+  --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+  +++ b/n1	* +0000 (glob)
+  @@ -0,0 +1,1 @@
+  +n1
+  $ hg prune -r . --keep
+  1 changesets pruned
+  $ hg status
+  ? n1
+
 test hg prune -B bookmark
 yoinked from test-mq-strip.t
 
--- a/tests/test-simple4server-bundle2.t
+++ b/tests/test-simple4server-bundle2.t
@@ -71,12 +71,12 @@
 Capacity testing
 ===================
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
@@ -128,42 +128,42 @@
 ===========================================
 (used by bitbucket to select which repo use evolve)
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)
 
   $ echo '[__temporary__]' >> server/.hg/hgrc
   $ echo 'advertiseobsolete=False' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol)
 
   $ echo 'advertiseobsolete=True' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream bundle2-exp=HG2Y%0Ab2x%253Achangegroup%3D01%2C02%0Ab2x%253Alistkeys%0Ab2x%253Aobsmarkers%3DV0%2CV1%0Ab2x%253Apushkey%0Ab2x%253Aremote-changegroup%3Dhttp%2Chttps%0Adigests%3Dmd5%2Csha1%2Csha512 unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)
--- a/tests/test-simple4server.t
+++ b/tests/test-simple4server.t
@@ -71,12 +71,12 @@
 Capacity testing
 ===================
 
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
@@ -129,42 +129,42 @@
 ===========================================
 (used by bitbucket to select which repo use evolve)
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)
 
   $ echo '[__temporary__]' >> server/.hg/hgrc
   $ echo 'advertiseobsolete=False' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 (no-eol)
 
   $ echo 'advertiseobsolete=True' >> server/.hg/hgrc
-  $ $TESTDIR/killdaemons.py
+  $ $TESTDIR/killdaemons.py $DAEMON_PIDS
   $ hg serve -R server -n test -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
   $ cat hg.pid >> $DAEMON_PIDS
 
-  $ curl --silent "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
+  $ wget -q -O - "http://localhost:$HGPORT/?cmd=listkeys&namespace=namespaces" | sort
   bookmarks	
   namespaces	
   obsolete	
   phases	
-  $ curl --silent http://localhost:$HGPORT/?cmd=hello
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=hello
   capabilities: lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon
-  $ curl --silent http://localhost:$HGPORT/?cmd=capabilities
+  $ wget -q -O - http://localhost:$HGPORT/?cmd=capabilities
   lookup changegroupsubset branchmap pushkey known getbundle unbundlehash batch stream unbundle=HG10GZ,HG10BZ,HG10UN httpheader=1024 _evoext_pushobsmarkers_0 _evoext_pullobsmarkers_0 _evoext_obshash_0 _evoext_getbundle_obscommon (no-eol)