Mercurial > hg > evolve
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)